Full Code of iwiniwin/LuaKit for AI

master 5237d57c9230 cached
37 files
136.2 KB
43.5k tokens
18 symbols
1 requests
Download .txt
Repository: iwiniwin/LuaKit
Branch: master
Commit: 5237d57c9230
Files: 37
Total size: 136.2 KB

Directory structure:
gitextract_vpofiopn/

├── .gitignore
├── README.md
├── _load.lua
├── build/
│   └── int64.c
├── core/
│   ├── component/
│   │   ├── component_base.lua
│   │   ├── component_extend.lua
│   │   └── component_factory.lua
│   ├── event/
│   │   ├── event.lua
│   │   └── event_system.lua
│   ├── object.lua
│   └── sandbox.lua
├── init.lua
├── lib/
│   ├── file.lua
│   ├── function.lua
│   ├── string.lua
│   ├── table.lua
│   └── time.lua
├── mvc/
│   ├── loader.lua
│   ├── module1/
│   │   ├── test1_ctr.lua
│   │   └── test1_view.lua
│   ├── module2/
│   │   ├── test2_ctr.lua
│   │   └── test2_view.lua
│   └── module_config.lua
├── pattern/
│   ├── AbstractFactoryPattern.lua
│   ├── AdapterPattern.lua
│   ├── CORPattern.lua
│   ├── CompositePattern.lua
│   ├── FactoryMethodPattern.lua
│   ├── ObserverPattern.lua
│   ├── ProxyPattern.lua
│   ├── StrategyPattern.lua
│   └── TemplatePattern.lua
├── test.lua
└── utils/
    ├── dump.lua
    ├── dump_to_file.lua
    ├── memory_monitor.lua
    └── profiler.lua

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
other/


================================================
FILE: README.md
================================================
# LuaKit
Lua核心工具包,提供对面向对象,组件系统,mvc模块化加载,事件分发系统等常用模式的封装。同时提供打印,内存泄漏检测,字符串操作等常用工具类。
PS:添加了注释的Lua源码可以参考<a href="https://github.com/iwiniwin/source-code/tree/master/lua-5.1.4/src">这里</a>

LuaKit部分特性介绍如下,用法/测试用例请参考<a href="test.lua">这里</a>

# Contents  
- [打印复杂表结构](#打印复杂表结构)  
- [组件系统](#组件系统)  
- [事件分发系统](#事件分发系统)  
- [面向对象封装](#面向对象封装)  
- [分模块加载](#分模块加载)  
- [性能分析](#性能分析)  
- [内存泄漏检测](#内存泄漏检测)  

### 打印复杂表结构
dump支持按照指定格式打印任意类型的数据
dump_to_file支持将数据序列化到文件
```lua
local data = {
    key1 = 34,
    key2 = "str",
    key3 = {
        key4 = {
            key5 = 56
        },
        key6 = 78
    }
}
dump(data, "this is a dump test")
```
输出结果如下:
```
- "this is a dump test" = {
-     "key1" = 34
-     "key2" = "str"
-     "key3" = {
-         "key4" = {
-             "key5" = 56
-         }
-         "key6" = 78
-     }
- }
```
### 组件系统
游戏开发中很多功能无法单纯靠继承实现,因为类继承会导致难以轻易改变结构,功能全都向上依赖,子类的数据爆炸,大量冗余数据和方法导致内存消耗过大。而采用组件系统,组件才是功能的携带者,可以实时增减,动态为对象增减功能。对象绑定组件就可以拥有该组件提供的功能,解绑组件则移除对应功能,通过组合构建拥有完整功能的对象,更加灵活解耦。

例如: Fly组件提供飞的能力,鸟对象绑定Fly组件就可以飞,移除Fly组件就不能飞
```lua
local ComponentBase = require("core.component.component_base")
local ComponentExtend = require("core.component.component_extend")

local A = class()
ComponentExtend(A)

-- 组件1
local Component1 = class(ComponentBase)
Component1.exportInterface = {
    {"test1"},
}
function Component1:test1( ... )
    dump("call test1 ...")
end

-- 组件2
local Component2 = class(ComponentBase)
Component2.exportInterface = {
    {"test2"},
}
function Component2:test2( ... )
    dump("call test2 ...")
end

local a = new(A)

a:bind_component(Component1)  -- 对象a绑定组件1 拥有test1方法
a:bind_component(Component2)  -- 对象a绑定组件2 拥有test2方法
a:test1()
a:test2()

a:unbind_component(Component1)  -- 解绑组件1 丧失test1方法
-- a:test1()  -- 报错 attempt to call method 'test1' (a nil value)
```
### 事件分发系统
基于观察者模式封装的一套事件分发系统

```lua
local EventSystem = new(require("core.event.event_system"))
local Event = require("core.event.event")

-- 简单用法
EventSystem:on("test", function ( ... )
    dump({...})
end)

EventSystem:emit("test", "param1", "param2")

-- 高级用法
local A = class()
function A:on_key_down( key )
    dump(key, "key name A")
end
EventSystem:on(Event.KeyDown, A.on_key_down, {target = A})

local B = class()
function B:on_key_down( key )
    dump(key, "key name B")

    return true  -- 可以中断事件派发
end

-- 后注册的事件通过提高优先级可以保证先被调用
EventSystem:on(Event.KeyDown, B.on_key_down, {target = B, priority = 2})

EventSystem:emit(Event.KeyDown, "Ctrl")

EventSystem:off_all(B)  -- 通过target取消注册

EventSystem:emit(Event.KeyDown, "Ctrl")
```
高级用法中,第一次emit时,首先触发B,B的回调返回true中断了派发,导致A的回调不会被执行,所以只打印了key name B
第二次emit时,B已经被off_all,不会触发B的回调,自然也没有人再中断事件的派发,所以只打印了key name A

输出结果如下所示:
```
- dump from: E:\Project\LuaKit\test.lua:155: in function 'func'
- "<var>" = {
-     1 = "param1"
-     2 = "param2"
- }
- dump from: E:\Project\LuaKit\test.lua:170: in function 'func'
- "key name B" = "Ctrl"
- dump from: E:\Project\LuaKit\test.lua:164: in function 'func'
- "key name A" = "Ctrl"
```

### 面向对象封装
基于Lua原表提供了`class`, `new`, `delete`等面向对象中思想中的常用函数
```lua
local Class1 = class()
function Class1:ctor( ... )
    dump("Class1:ctor")
end
function Class1:dtor( ... )
    dump("Class1:dtor")
end

-- Class2集成于Class1
local Class2 = class(Class1)
function Class2:ctor( ... )
    dump("Class2:ctor")
end
function Class2:dtor( ... )
    dump("Class2:dtor")
end
-- 实例化对象
local c1 = new(Class1)
local c2 = new (Class2)
-- 销毁对象
delete(c1)
delete(c2)
```

### 分模块加载
分模块加载是一种模块化设计思想,通过配置文件,实现对模块的按需加载。再结合mvc,可以将一个大系统,看做由多个子模块组合而成。配置了指定模块,则系统拥有该模块的功能,取消配置则不会加载该模块。
```lua
local ModuleConfig = {}

ModuleConfig.Module1 = {
    file = "mvc.module1.test1_view",
    initOrder = 2,  -- 配置模块加载顺序
}

ModuleConfig.Module2 = {
    file = "mvc.module2.test2_view",
    initOrder = 1,
}

return ModuleConfig
```

### 性能分析
通过LuaKit提供的profile工具,可以获取函数的调用情况,调用次数,调用时间,子函数调用时间等信息,以此来分析是否存在异常的函数调用或耗时操作。在需要进行性能优化时十分有用。
```lua
local new_profiler = require("utils.profiler")
local profiler = new_profiler("call")
profiler:start()  -- 开启性能分析

local function aaa(  )
    for i = 1, 10000000 do

    end
end
local function ttt(  )
    aaa()
end
ttt()
-- 同时支持分析协程内的函数调用情况
local co = coroutine.create(function ( ... )
    aaa()
end)
coroutine.resume(co)

profiler:stop()  -- 停止性能分析
-- 输出分析结果到文件
profiler:dump_report_to_file("profile.txt")
```
分析结果部分内容如下
```
Total time spent in profiled functions: 0.113s
Total call count spent in profiled functions: 12
Lua Profile output created by profiler.lua. author: iwiniwin 

-----------------------------------------------------------------------------------
| FILE                            : FUNCTION         : TIME   : %     : Call count|
-----------------------------------------------------------------------------------
| L:E:\Project\LuaKit\test.lua:61 : aaa              : 0.1130 : 100.0 :         2 |
| L:E:\Project\LuaKit\test.lua:71 : unknow           : 0.0580 : 51.3  :         1 |
| C:resume@=[C]:-1                : resume           : 0.0580 : 51.3  :         1 |
| L:E:\Project\LuaKit\test.lua:66 : ttt              : 0.0550 : 48.7  :         1 |
| C:insert@=[C]:-1                : insert           : ~      : ~     :         1 |
| C:sethook@=[C]:-1               : sethook          : ~      : ~     :         2 |
| C:coroutine_create@=[C]:-1      : coroutine_create : ~      : ~     :         1 |
| L:.\utils\profiler.lua:122      : stop             : ~      : ~     :         1 |
| C:clock@=[C]:-1                 : clock            : ~      : ~     :         1 |
| L:.\utils\profiler.lua:106      : create           : ~      : ~     :         1 |
-----------------------------------------------------------------------------------

--------------------- L:aaa@@E:\Project\LuaKit\test.lua:61 ---------------------
Call count:            2
Time spend total:       0.1130s
Time spent in children: 0.0000s
Time spent in self:     0.1130s
```

### 内存泄漏检测
对于游戏开发而言,内存泄露往往是最容易忽视的问题,很多开发者并不知道自己的代码是否存在内存泄露。此类问题可以借助MemoryMonitor来检测,具体原理是借助lua的弱引用,把某个需要观察的对象加入到弱表,如果不存在外部引用,那么在gc时候,弱表上的该对象也就自然消失,如果弱表还存在该对象,说明外部仍存在引用。
```lua
local MemoryMonitor = require("utils.memory_monitor")
local memoryMonitor = new(MemoryMonitor)

a = {}
function test( ... )
    local b = {xxx = "xxx"}
    a.b = b
    memoryMonitor:add_to_leak_monitor(b, "b")  --将b添加到内存检测工具,此时a没有被释放掉 则b也释放不掉
end
test()

-- 由于a在引用b,因此b存在内存泄漏
memoryMonitor:update()  -- 这里会打印日志

-- a不再引用b,b也被释放
a = nil
memoryMonitor:update()  -- 没有内存泄漏,这里不会打印日志
```
第一次update时存在内存泄漏,输出如下所示
```
存在以下内存泄漏:    
b@table: 02C5F7A8 = table: 02C5F7A8
请仔细检查代码!!!
```


================================================
FILE: _load.lua
================================================
--[[--
LuaKit入口
@module _load
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:57:21
]]
require("init")()

dump("Hello LuaKit")

-- require("test")  -- 运行测试用例

--[[
    什么叫组合 Composition
    在类中增加一个私有域,引用另一个已有的类的实例,通过调用实例的方法从而获得新的功能
]]

================================================
FILE: build/int64.c
================================================
#include <lua.h>
#include <lauxlib.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>

// 将指定索引处的值转换为int64
static int64_t
_int64(lua_State *L, int index) {
	int type = lua_type(L,index);
	int64_t n = 0;
	switch(type) {
	case LUA_TNUMBER: {
		// lua number 类型直接强转
		lua_Number d = lua_tonumber(L,index);
		n = (int64_t)d;
		break;
	}
	case LUA_TSTRING: {
		size_t len = 0;
		const uint8_t * str = (const uint8_t *)lua_tolstring(L, index, &len);
		if (len>8) {
			return luaL_error(L, "The string (length = %d) is not an int64 string", len);
		}
		int i = 0;
		uint64_t n64 = 0;
		// 最多8个字节字符串,每个字符1个字节对应8位,将每个字符对应的8位拼起来
		for (i=0;i<(int)len;i++) {
			n64 |= (uint64_t)str[i] << (i*8);
		}
		n = (int64_t)n64;
		break;
	}
	case LUA_TLIGHTUSERDATA: {
		// 指针地址值就是int64值
		void * p = lua_touserdata(L,index);
		n = (intptr_t)p;
		break;
	}
	default:
		return luaL_error(L, "argument %d error type %s", index, lua_typename(L,type));
	}
	return n;
}

// 入栈一个int64,转换成指针入栈的
static inline void
_pushint64(lua_State *L, int64_t n) {
	void * p = (void *)(intptr_t)n;
	lua_pushlightuserdata(L,p);
}

static int
int64_add(lua_State *L) {
	int64_t a = _int64(L,1);
	int64_t b = _int64(L,2);
	_pushint64(L, a+b);
	
	return 1;
}

static int
int64_sub(lua_State *L) {
	int64_t a = _int64(L,1);
	int64_t b = _int64(L,2);
	_pushint64(L, a-b);
	
	return 1;
}

static int
int64_mul(lua_State *L) {
	int64_t a = _int64(L,1);
	int64_t b = _int64(L,2);
	_pushint64(L, a * b);
	
	return 1;
}

static int
int64_div(lua_State *L) {
	int64_t a = _int64(L,1);
	int64_t b = _int64(L,2);
	if (b == 0) {
		return luaL_error(L, "div by zero");
	}
	_pushint64(L, a / b);
	
	return 1;
}

static int
int64_mod(lua_State *L) {
	int64_t a = _int64(L,1);
	int64_t b = _int64(L,2);
	if (b == 0) {
		return luaL_error(L, "mod by zero");
	}
	_pushint64(L, a % b);
	
	return 1;
}

static int64_t
_pow64(int64_t a, int64_t b) {
	if (b == 1) {
		return a;
	}
	int64_t a2 = a * a;
	if (b % 2 == 1) {
		return _pow64(a2, b/2) * a;
	} else {
		return _pow64(a2, b/2);
	}
}

static int
int64_pow(lua_State *L) {
	int64_t a = _int64(L,1);
	int64_t b = _int64(L,2);
	int64_t p;
	if (b > 0) {
		p = _pow64(a,b);
	} else if (b == 0) {
		p = 1;
	} else {
		return luaL_error(L, "pow by nagtive number %d",(int)b);
	} 
	_pushint64(L, p);

	return 1;
}

static int
int64_unm(lua_State *L) {
	int64_t a = _int64(L,1);
	_pushint64(L, -a);
	return 1;
}

// 构建int64
static int
int64_new(lua_State *L) {
	int top = lua_gettop(L);
	int64_t n;
	switch(top) {
		case 0 : 
			lua_pushlightuserdata(L,NULL);
			break;
		case 1 :
			n = _int64(L,1);
			_pushint64(L,n);
			break;
		default: {
			int base = luaL_checkinteger(L,2);  // 第二个参数,指明几进制
			if (base < 2) {
				luaL_error(L, "base must be >= 2");
			}
			const char * str = luaL_checkstring(L, 1);
			n = strtoll(str, NULL, base);  // 将字符串根据base转换成长整型数
			_pushint64(L,n);
			break;
		}
	}
	return 1;
}

static int
int64_eq(lua_State *L) {
	int64_t a = _int64(L,1);
	int64_t b = _int64(L,2);
	printf("%s %s\n",lua_typename(L,1),lua_typename(L,2));
	printf("%ld %ld\n",a,b);
	lua_pushboolean(L,a == b);
	return 1;
}

static int
int64_lt(lua_State *L) {
	int64_t a = _int64(L,1);
	int64_t b = _int64(L,2);
	lua_pushboolean(L,a < b);
	return 1;
}

static int
int64_le(lua_State *L) {
	int64_t a = _int64(L,1);
	int64_t b = _int64(L,2);
	lua_pushboolean(L,a <= b);
	return 1;
}

static int
int64_len(lua_State *L) {
	int64_t a = _int64(L,1);
	lua_pushnumber(L,(lua_Number)a);
	return 1;
}

static int
tostring(lua_State *L) {
	static char hex[16] = "0123456789ABCDEF";
	uintptr_t n = (uintptr_t)lua_touserdata(L,1);
	if (lua_gettop(L) == 1) {
		// 只传了n, 不带base处理
		luaL_Buffer b;
		// 开辟一个字符串缓存,预分配28大小的空间
		luaL_buffinitsize(L , &b , 28);
		luaL_addstring(&b, "int64: 0x");
		int i;
		bool strip = true;
		// 将int64转换为十六进制显示
		for (i=15;i>=0;i--) {
			int c = (n >> (i*4)) & 0xf;
			if (strip && c ==0) {
				continue;
			}
			strip = false;
			luaL_addchar(&b, hex[c]);
		}
		if (strip) {
			luaL_addchar(&b , '0');
		}
		luaL_pushresult(&b);
	} else {
		// 带base处理
		int base = luaL_checkinteger(L,2);
		int shift , mask;
		switch(base) {
		case 0: {
			unsigned char buffer[8];
			int i;
			for (i=0;i<8;i++) {
				buffer[i] = (n >> (i*8)) & 0xff;
			}
			lua_pushlstring(L,(const char *)buffer, 8);
			return 1;
			}
		case 10: {
			// int64转换为10进制显示
			int64_t dec = (int64_t)n;
			luaL_Buffer b;
			luaL_buffinitsize(L , &b , 28);
			if (dec<0) {
				luaL_addchar(&b, '-');
				dec = -dec;
			}
			int buffer[32];
			int i;
			for (i=0;i<32;i++) {
				buffer[i] = dec%10;
				dec /= 10;
				if (dec == 0)
					break;
			}
			while (i>=0) {
				luaL_addchar(&b, hex[buffer[i]]);
				--i;
			}
			luaL_pushresult(&b);
			return 1;
		}
		case 2:
			shift = 1;
			mask = 1;
			break;
		case 8:
			shift = 3;
			mask = 7;
			break;
		case 16:
			shift = 4;
			mask = 0xf;
			break;
		default:
			luaL_error(L, "Unsupport base %d",base);
			break;
		}
		int i;
		char buffer[64];
		for (i=0;i<64;i+=shift) {
			buffer[i/shift] = hex[(n>>(64-shift-i)) & mask];
		}
		lua_pushlstring(L, buffer, 64 / shift);
	}
	return 1;
}

static void
make_mt(lua_State *L) {
	// 创建包含以下函数的元表
	luaL_Reg lib[] = {
		{ "__add", int64_add },
		{ "__sub", int64_sub },
		{ "__mul", int64_mul },
		{ "__div", int64_div },
		{ "__mod", int64_mod },
		{ "__unm", int64_unm },
		{ "__pow", int64_pow },
		{ "__eq", int64_eq },
		{ "__lt", int64_lt },
		{ "__le", int64_le },
		{ "__len", int64_len },
		{ "__tostring", tostring },
		{ NULL, NULL },
	};
	luaL_newlib(L,lib);
}

int
luaopen_int64(lua_State *L) {
	// 64位机器上指针占8字节,32位机器上指针占用4字节
	if (sizeof(intptr_t)!=sizeof(int64_t)) {
		return luaL_error(L, "Only support 64bit architecture");
	}
	lua_pushlightuserdata(L,NULL);
	make_mt(L);
	lua_setmetatable(L,-2);
	lua_pop(L,1);

	lua_newtable(L);
	lua_pushcfunction(L, int64_new);
	lua_setfield(L, -2, "new");
	lua_pushcfunction(L, tostring);
	lua_setfield(L, -2, "tostring");

	return 1;
}

================================================
FILE: core/component/component_base.lua
================================================
--[[--组件基类  
注意:所有组件必须继承该类
@module ComponentBase
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:45:20
]]
local ComponentBase = class()

ComponentBase._class_name = "ComponentBase"

ComponentBase.exportInterface = {

}

--[[--
        组件依赖列表 
    ]]
ComponentBase.depends = {}

--[[--
    组件优先级 
]]
ComponentBase.priority = 1;

function ComponentBase:ctor( componentName )
    --[[--
        组件名称
    ]]
    self.name = componentName or "Component";
end

--[[--
    组件绑定对象
    @tparam Object object 需要绑定组件的“宿主”
    
]]
function ComponentBase:bind( object )
    for i,v in ipairs(self.exportInterface) do
        object:bind_method(self, v[1],   handler(self, self[v[1]]), v[2], v[3]);
    end 
end

--[[--
    组件解绑对象
    @tparam Object object 需要解除绑定的“宿主”
    
]]
function ComponentBase:unbind( object )
    for i,v in ipairs(self.exportInterface) do
        object:unbind_method(self, v[1]);
    end 
end

--[[--
    重置组件,按优先级调用,需要注意的是reset是配合priority使用priority越高组件越先执行
    @tparam Object object 需要解除绑定的“宿主”

]]
function ComponentBase:reset( object )
end

return ComponentBase

================================================
FILE: core/component/component_extend.lua
================================================
--[[--组件扩展
赋予类绑定解绑组件的能力
@module ComponentExtend
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:45:20
]]
local ComponentFactory = import("core.component.component_factory")

local component_extend = function ( Class )

    function Class:has_component( ComponentClass )
        local component_name = tostring(ComponentClass)
        return self._component_objects and self._component_objects[component_name]
    end

    function Class:get_component( ComponentClass )
        if not self._component_objects then return end
        local component_name = tostring(ComponentClass)
        return self._component_objects[component_name]
    end

    function Class:bind_component( ComponentClass )
        assert(ComponentClass, "required component class")
        local component_name = tostring(ComponentClass);
        if not self._component_objects then self._component_objects = {} end
        if self._component_objects[component_name] then return end

        local component = ComponentFactory.create_component(ComponentClass);
        for i,DependComponentClass in ipairs(component.depends) do
            
            self:bind_component(DependComponentClass)

            local depend_component_name = tostring(DependComponentClass)
            if not self._component_depends then self._component_depends = {} end
            if not self._component_depends[depend_component_name] then
                self._component_depends[depend_component_name] = {}
            end
            
            table.insert(self._component_depends[depend_component_name], component_name)
        end

        component.object = self
        component:bind(self)
        self._component_objects[component_name] = component
        return component
    end

    function Class:unbind_component( ComponentClass )
        local component_name = tostring(ComponentClass);
        assert(self._component_objects and self._component_objects[component_name],
            string.format("component %s not binding", component_name))
        assert(not self._component_depends or not self._component_depends[component_name],
            string.format("component %s depends by other binding", component_name))

        local component = self._component_objects[component_name]
        for i,DependComponentClass in ipairs(component.depends) do
            local depend_component_name = tostring(DependComponentClass)
            for i,name in ipairs(self._component_depends[depend_component_name]) do
                if name == component_name then
                    table.remove(self._component_depends[depend_component_name], i)
                    if #self._component_depends[depend_component_name] == 0 then
                        self._component_depends[depend_component_name] = nil
                    end
                    break
                end
            end
        end

        component:unbind(self)
        self._component_objects[component_name] = nil
        component.object = nil
        delete(component)
    end

    function Class:unbind_all_component(  )
        if not self._component_objects then return end
        for component_name,component in pairs(self._component_objects) do
            component:unbind(self)
            component.object = nil
            delete(component)
        end
        self._component_objects = {}
        self._component_depends = {}
    end

    function Class:bind_method( component, method_name, method, deprecate_origin_method, call_origin_method_last )
        
        if not self._bind_methods then
            self._bind_methods = {}
        end

        if not self._bind_methods[method_name] then
            self._bind_methods[method_name] = {}
        end

        -- 根据优先级确定绑定
        local index
        local component_priority = component.priority or 1
        for i,c in ipairs(self._bind_methods[method_name]) do
            local priority = c[1].priority or 1
            if component_priority > priority then
                index = i
                break
            end 
        end

        local chain = { component }

        local new_method

        if index then
            chain[2] = self._bind_methods[method_name][index][2]
        else
            chain[2] = self[method_name]
        end
        
        if deprecate_origin_method then
            new_method = method
        elseif call_origin_method_last then
            new_method = function ( ... )
                if chain[2] then
                    method(...)
                    return chain[2](...)
                else
                    return method(...)
                end
            end
        else
            new_method = function ( ... )
                local func = chain[2]
                if func then
                    local ret = func(...)
                    if ret then
                        local args = {...}
                        args[#args + 1] = ret
                        return method(unpack(args))
                    else
                        return method(...)
                    end
                else
                    return method(...)
                end
            end
        end

        if index then
            self._bind_methods[method_name][index][2] = new_method
            table.insert(self._bind_methods[method_name], index, chain)
        else
            self[method_name] = new_method
            table.insert(self._bind_methods[method_name], chain)
        end

        chain[3] = new_method
    end

    function Class:unbind_method( component, method_name )
        if not self._bind_methods or not self._bind_methods[method_name] then
            return
        end
        local methods = self._bind_methods[method_name]
        local count = #methods
        for i = count, 1, -1 do
            local chain = methods[i]
            if chain[1] == component then
                if i < count then
                    methods[i + 1][2] = chain[2]
                elseif count > 1 then
                    self[method_name] = methods[i - 1][3]
                elseif count == 1 then
                    self[method_name] = chain[2]
                    self._bind_methods[method_name] = nil
                end
                table.remove(methods, i)
                break;
            end
        end
    end

end

return component_extend

================================================
FILE: core/component/component_factory.lua
================================================
--[[--组件工厂类  
@module ComponentFactory
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:45:20
]]
local ComponentBase = import("core.component.component_base")

local ComponentFactory = {}

function ComponentFactory.create_component( component )
    assert(component ~= nil, "invalid component : " .. tostring(component))
    if typeof(component, ComponentBase) then
        return new(component);
    elseif type(component) == "table" and component.require and component.path then
        return new(component.require(component.path))
    else
        error("invalid component : " .. tostring(component))
    end
end

return ComponentFactory


================================================
FILE: core/event/event.lua
================================================
--[[--事件配置,便于统一管理项目中所有使用的事件
@module Event
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-04-01 14:47:14
]]
local Event = {}

local event_id = 1;

Event.get_unique_id = function (  )
    event_id = event_id + 1
    return "event_" .. event_id
end

Event.KeyDown           = Event.get_unique_id()

return Event


================================================
FILE: core/event/event_system.lua
================================================
--[[--事件分发系统
@module EventSystem
@author iwiniwin

Date   2020-03-30 14:23:50
Last Modified by   iwiniwin
Last Modified time 2020-04-01 16:18:51
]]
local EventSystem = class()

local id = 1

EventSystem._class_name = "EventSystem"


function EventSystem:ctor(  )
    self._listeners = {}
end

function EventSystem:dtor(  )
    self._listeners = nil
end

--[[
    注册监听一个事件
]]
function EventSystem:on( event, func, params )
    event = tostring(event)
    if not self._listeners[event] then
        self._listeners[event] = {list = {}, emit_count = 0}
    end
    local event_listener = self._listeners[event]

    params = params or {}
    local priority = params.priority or 0
    local target = params.target
    
    for i,cb in ipairs(event_listener.list) do
        if cb.target == target and cb.func == func then
            error("register the same callback multiple times")
        end
    end

    local cb = {target = target, func = func, id = id, priority = priority}
    table.insert(event_listener.list, cb)

    id = id + 1

    if priority > 0 then
        event_listener.need_sort = true
        self:sort(event_listener)
    end
end

--[[
    反注册事件,取消对一个事件的监听
]]
function EventSystem:off( event, func, params )
    event = tostring(event)
    if not self._listeners[event] then
        return 
    end
    local event_listener = self._listeners[event]

    params = params or {}

    for i,cb in ipairs(event_listener.list) do
        if cb.func == func and cb.target == params.target then
            if event_listener.emit_count > 0 then
                -- 派发过程中只进行标记删除
                cb.need_remove = true
                event_listener.need_clean = true
            else
                table.remove(event_listener.list, i)
            end
            break;
        end
    end
end

--[[
    根据target移除所有其注册的事件监听
]]
function EventSystem:off_all( target )
    for event,listener in pairs(self._listeners) do

        for i,cb in ipairs(listener.list) do
            if cb.target == target then
                cb.need_remove = true
            end
        end

        listener.need_clean = true
        self:clean(listener)
    end
end

--[[
    派发一个事件
]]
function EventSystem:emit( event, ... )
    event = tostring(event)
    if not self._listeners[event] then
        return 
    end
    local event_listener = self._listeners[event]

    local interrupt = false

    -- 这里不能使用ipairs,确保不会触发在派发过程中注册的事件
    -- 只取当前已经注册的事件数量,如果在派发过程中再注册 则当次不会调用
    local length = #event_listener.list
    for i = 1, length do
        if interrupt == true then
            break
        end
        local cb = event_listener.list[i]
        if cb.func and cb.need_remove ~= true then
            -- 这里必须使用count计数形式判断是否处于派发过程中
            -- 如果仅使用true/false标记,在派发的回调中又进行派发,会导致标记错误
            event_listener.emit_count = event_listener.emit_count + 1
            if cb.target then
                interrupt = cb.func(cb.target, ...)
            else
                interrupt = cb.func(...)
            end
            event_listener.emit_count = event_listener.emit_count - 1
        end
    end

    self:sort(event_listener);
    self:clean(event_listener);

    return interrupt
end

function EventSystem:sort( listener )
    --[[
        在派发过程中不会进行优先级排序
        比如当前事件有4个回调 1, 2, 3, 4,已经执行到3回调
        如果在3回调中又注册了一个优先级最高的回调,需要往第一个插入,此时会导致3回调又被调用一次
    ]]
    if listener.need_sort == true and listener.emit_count == 0 then

        table.sort(listener.list, function ( a, b )
            if a.priority == b.priority then
                return a.id < b.id
            else
                return a.priority > b.priority
            end
        end)

        listener.need_sort = false;
    end
end

function EventSystem:clean( listener )
    --[[
        在派发过程中不会进行移除
        比如当前事件有4个回调 1, 2, 3, 4,已经执行到3回调
        如果在3回调中移除了自己,则会导致4回调不会被触发
    ]]
    if listener.need_clean == true and listener.emit_count == 0 then
        for i = #listener.list, 1, -1 do
            if listener.list[i].need_remove then
                table.remove(listener.list, i)
            end
        end
        listener.need_clean = false;
    end
end

--[[
    修改已经注册的一个事件监听的优先级
]]
function EventSystem:update_priority( event, func, params )
    event = tostring(event)
    if not self._listeners[event] then
        return 
    end
    local event_listener = self._listeners[event]

    params = params or {}
    local priority = params.priority or 0
    for i,cb in ipairs(event_listener.list) do
        if cb.func == func and cb.target == params.target then
            cb.priority = priority
            event_listener.need_sort = true
            self:sort(event_listener);
            break;
        end
    end
end

return EventSystem

================================================
FILE: core/object.lua
================================================
--[[--用于模拟面向对象
@usage require("core/object")
@module object
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:45:13
]]
--------------------------------------------------------------------------------

-- Description: Provide object mechanism for lua
-- Note for the object model here:
--		1.The feature like C++ static members is not support so perfect.
--		What that means is that if u need something like c++ static members,
--		U can access it as a rvalue like C++, but if u need access it
--		as a lvalue u must use [class.member] to access,but not [object.member].
--		2.The function delete cannot release the object, because the gc is based on
--		reference count in lua.If u want to relase all the object memory, u have to
--      set the obj to nil to enable lua gc to recover the memory after calling delete.


---------------------Global functon class ---------------------------------------------------
--Parameters:   super               -- The super class
--              autoConstructSuper   -- If it is true, it will call super ctor automatic,when
--                                      new a class obj. Vice versa.
--Return    :   return an new class type
--Note      :   This function make single inheritance possible.
---------------------------------------------------------------------------------------------

---
-- 用于定义一个类.
--
-- @param #table super 父类。如果不指定,则表示不继承任何类,如果指定,则该指定的对象也必须是使用class()函数定义的类。
-- @param #boolean autoConstructSuper 是否自动调用父类构造函数,默认为true。如果指定为false,若不在ctor()中手动调用super()函数则不会执行父类的构造函数。
-- @return #table class 返回定义的类。
-- @usage
-- Human = class()
-- Human.ctor = function(self)
--  self.m_type = "human"
-- end
-- Human.dtor = function(self)
--  print_string("deleted")
-- end
-- Human.speak = function(self)
--  print_string("I am a " .. self.m_type)
-- end
--
-- Man = class(Human, true)
-- Man.ctor = function(self, name)
--  self.m_sex = "m"
--  self.m_name = name
-- end

function class( super, autoConstructSuper )
    local classType = {
        autoConstructSuper = autoConstructSuper or (autoConstructSuper == nil)
    }
    if super then
        classType.super = super
        local mt = getmetatable(super)
        setmetatable(classType, {__index = super})
    else
        classType.setDelegate = function( self, delegate )
            self.m_delegate = delegate
        end
    end
    return classType
end

---------------------Global functon super ----------------------------------------------
--Parameters:   obj         -- The current class which not contruct completely.
--              ...         -- The super class ctor params.
--Return    :   return an class obj.
--Note      :   This function should be called when newClass = class(super,false).
-----------------------------------------------------------------------------------------

---
-- 手动调用父类的构造函数.
-- 只有当定义类时采用class(super,false)的调用方式时才可以调用此方法,若此时不手动调用则不会执行父类的构造函数。
-- **只能在子类的构造函数中调用。**
-- @param #table obj 类的实例。
-- @param ... 父类构造函数需要传入的参数。
-- @usage
-- local baseClass = class()
-- local derivedClass = class(baseClass,false)
-- derivedClass.ctor = function()
--     super(self) - -此处如果不手动调用super()则不会执行基类的ctor()
-- end
function super(obj, ...)
  do
    local create;
    create =
    function(c, ...)
      if c.super and c.autoConstructSuper then
        create(c.super, ...);
      end
      if rawget(c,"ctor") then
        obj.currentSuper = c.super;
        c.ctor(obj, ...);
      elseif rawget(c, "__init__") then
        obj.currentSuper = c.super;
        c.__init__(obj, ...);
      end
    end

    create(obj.currentSuper, ...);
  end
end

---------------------Global functon new -------------------------------------------------
--Parameters: 	classType -- Table(As Class in C++)
-- 				...		   -- All other parameters requisted in constructor
--Return 	:   return an object
--Note		:	This function is defined to simulate C++ new function.
--				First it called the constructor of base class then to be derived class's.
-----------------------------------------------------------------------------------------

---
-- 创建一个类的实例.
-- 调用此方法时会按照类的继承顺序,自上而下调用每个类的构造函数,并返回新创建的实例。
--
-- @param #table classType 类名。  使用class()返回的类。
-- @param ... 构造函数需要传入的参数。
-- @return #table obj 新创建的实例。
-- @usage
-- local me = new(Man, "zzp")
-- me:speak()
function new(classType, ...)
  local obj = {};
  setmetatable(obj, { __index = classType, __object=1});
  do
    local create;
    create =
    function(c, ...)
      if c.super and c.autoConstructSuper then
        create(c.super, ...);
      end
      if rawget(c,"ctor") then
        obj.currentSuper = c.super;
        c.ctor(obj, ...);
      elseif rawget(c,"__init__") then
        obj.currentSuper = c.super;
        c.__init__(obj, ...);
      end

    end

    create(classType, ...);
  end
  obj.currentSuper = nil;
  return obj;
end

---------------------Global functon delete ----------------------------------------------
--Parameters: 	obj -- the object to be deleted
--Return 	:   no return
--Note		:	This function is defined to simulate C++ delete function.
--				First it called the destructor of derived class then to be base class's.
-----------------------------------------------------------------------------------------

---
-- 删除某个实例.
-- 类似c++里的delete ,会按照继承顺序,依次自下而上调用每个类的析构方法。
--
-- **需要留意的是,删除此实例后,lua里该对象的引用(obj)依然有效,再次使用可能会发生无法预知的意外。**
--
-- @param #table obj 需要删除的实例。
function delete(obj)
  do
    local destory =
    function(c)
      while c do
        if rawget(c,"dtor") then
          c.dtor(obj);
        end

        c = getmetatable(c);
        c = c and c.__index;
      end
    end
    destory(obj);
  end
end

---------------------Global functon delete ----------------------------------------------
--Parameters:   class       -- The class type to add property
--              varName     -- The class member name to be get or set
--              propName    -- The name to be added after get or set to organize a function name.
--              createGetter-- if need getter, true,otherwise false.
--              createSetter-- if need setter, true,otherwise false.
--Return    :   no return
--Note      :   This function is going to add get[PropName] / set[PropName] to [class].
-----------------------------------------------------------------------------------------

---
-- 为类定义一个property (java里的getter/setter).
-- 会自动为类生成getter/setter方法。
--
-- @param #table class 使用class()方法定义的类。
-- @param #string varName 类里的成员变量名。
-- @param #string propName 属性名,也就是生成的方法setXX/getXX里的'XX'。
-- @param #boolean createGetter 是否生成getter。
-- @param #boolean createSetter 是否生成setter。<br>
-- 如果createGetter不为false或nil,则给class生成一个get#propName()方法,可以获取class的varName的值。<br>
-- 如果createSetter不为false或nil,则给class生成一个set#propName(Value)方法,可以设置class的varName为Value。
-- @usage
-- property(Man, "m_name", "Name", true, false)
-- local me = new(Man, "zzp")
-- print_string(me:getName())
function property(class, varName, propName, createGetter, createSetter)
  createGetter = createGetter or (createGetter == nil);
  createSetter = createSetter or (createSetter == nil);

  if createGetter then
    class[string.format("get%s",propName)] = function(self)
      return self[varName];
    end
  end

  if createSetter then
    class[string.format("set%s",propName)] = function(self,var)
      self[varName] = var;
    end
  end
end

---------------------Global functon delete ----------------------------------------------
--Parameters:   obj         -- A class object
--              classType   -- A class
--Return    :   return true, if the obj is a object of the classType or a object of the
--              classType's derive class. otherwise ,return false;
-----------------------------------------------------------------------------------------

---
-- 判断一个对象是否是某个类(包括其父类)的实例.
-- 类似java里的instanceof。
--
-- @param obj 需要判断的对象。
-- @param classType 使用class()方法定义的类。
-- @return #boolean 若obj是classType的实例,则返回true;否则,返回false。
-- @usage
-- local me = new(Man, "zzp")
-- if typeof(me, Man) == true then
--     print_string("me is instance of Man")
-- end
function typeof(obj, classType)
  if type(obj) ~= type(table) or type(classType) ~= type(table) then
    return type(obj) == type(classType);
  end

  while obj do
    if obj == classType then
      return true;
    end
    obj = getmetatable(obj) and getmetatable(obj).__index;
  end

  return false;
end

---------------------Global functon delete ----------------------------------------------
--Parameters:   obj         -- A class object
--Return    :   return the object's type class.
-----------------------------------------------------------------------------------------

---
-- 通过一个对象反向得到此对象的类.
--
-- @param obj 对象。
-- @return class 此对象的类。
-- @return #nil 如果obj不是某个类的对象,则返回nil。
function decltype(obj)
  if type(obj) ~= type(table) or obj.autoConstructSuper == nil then
    --error("Not a class obj");
    return nil;
  end

  if rawget(obj,"autoConstructSuper") ~= nil then
    --error("It is a class but not a class obj");
    return nil;
  end

  local class = getmetatable(obj) and getmetatable(obj).__index;
  if not class then
    --error("No class reference");
    return nil;
  end

  return class;
end

function mkproperty(getter, setter)
    return setmetatable({}, {
        __get = getter,
        __set = setter,
    })
end


================================================
FILE: core/sandbox.lua
================================================
--[[--lua沙盒
@module sandbox
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:45:08
]]
--[[
2. lua沙盒
理解:lua沙盒就是通过改变上下文环境,使函数可以在不同的环境表中运行,访问得到限制,从而避免相互
影响。可以利用其构建一个安全的环境,用来执行一些未知的危险代码
]]


function test( ... )
    print("hello")
end

test()

-- 设置test在环境表e运行,此时test不能调用_G中的print
-- local e = {}
-- setfenv(test, {})
-- test()
-- setfenv(test, _G)
-- test()


-- 在e环境表对x赋值不会影响到f环境
local e = {print = print, setfenv = setfenv}
setfenv(1, e)
x = 3
print(x)
local f = {print = print, setfenv = setfenv}
setfenv(1, f)
print(x)

---保护环境 人人有责
local env = getfenv();
local protectEnv = function(env)
    local mt  = getmetatable(env);
    local cache = {};
    mt.__newindex = function( t,k,v )
        if cache[k] == nil then
            cache[k] = v;
        else
            if cache[k] ~= v then
                error("不允许复写" .. k)
            end
        end
        rawset(mt.__index, k, v)
    end
end
protectEnv(env);

-- http://timothyqiu.com/archives/lua-note-sandboxes/

================================================
FILE: init.lua
================================================
--[[--
LuaKit初始化函数,初始化全局变量
@module init
@author iwiniwin

Date   2021-09-04 20:20:39
Last Modified by   iwiniwin
Last Modified time 2021-09-04 20:20:39
]]
local init = function ( requirePrefix )

	if requirePrefix then
		import = function ( modname )
			return require(requirePrefix .. modname)
		end
	else
		import = require
	end

	import("core.object")
	import("lib.string")
	local func_lib = import("lib.function")

	time = import("lib.time")

	dump = import("utils.dump")


	dump_to_file = import("utils.dump_to_file").dump_to_file

	handler = func_lib.handler

end

return init

================================================
FILE: lib/file.lua
================================================
--[[--文件操作
@module File
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:45:04
]]
local File = {}

---插入文件内容到指定行
--@table content 插入内容
--@string path 目标文件
--@number row 行数,默认插入到文件末尾
File.insert_to_file = function (content, path, row)
	file = io.open(path, "r")
	local lines = {}
	for line in file:lines() do
		if row and #lines == row - 1 then
			for _, l in ipairs(content) do
				table.insert(lines, l)
			end
		end
		table.insert(lines, line)
	end 
	file:close()
	if not row then
		for _, l in ipairs(content) do
			table.insert(lines, l)
		end
	end
	file = io.open(path, "w+")
	for _,line in ipairs(lines) do
		file:write(line .. "\n")
	end
	file:close()
end

---查询文件是否包含指定内容
--@string path 目标文件
--@string text 关键字
--@return 关键字所在行数,不存在返回空表
File.find_text = function (path, text)
	local result = {}
	local row = 0
	file = io.open(path, "r")
    for line in file:lines() do
    	row = row + 1
        if string.find(line, text) then
        	table.insert(result, row)
        end
    end 
    return result
end

return File

================================================
FILE: lib/function.lua
================================================
--[[--常用函数集合
@module Function
@author iwiniwin

Date   2020-02-27 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:45:04
]]
local M = {}

function M.handler( obj, method )
    return function ( ... )
        if obj and method then
            method(obj, ...)
        end
    end
end

return M

================================================
FILE: lib/string.lua
================================================
--[[--字符串操作
@module string
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:44:59
]]


-- local string = {};
---Allows the ability to index into a string using square-bracket notation
-- For example:
--		s = "hello"
--		s[1] = "h"
getmetatable('').__index = function(str, i)
	if (type(i) == 'number') then
		return string.sub(str, i, i)
	end
	
	return string[i]
end
 

--- Allows the ability to index into a string like above, but using normal brackes to
-- return the substring
-- For example:
--		s = "hello"
--		s(2,5) = "ello"
--
-- However, it also allows indexing into the string to return the byte (unicode) value
-- of the character found at the index. This only occurs if the second value is omitted
-- For example:
--      s = "hello"
--      s(2) = 101 (e)
--
-- Furthermore, it also allows for the ability to replace a character at the given index
-- with the given characters, iff the second value is a string
-- For example:
--		s = "hello"
--		s(2,'p') = "hpllo"
getmetatable('').__call = function(str, i, j)
	if (type(i) == 'number' and type(j) == 'number') then
		return string.sub(str, i, j)
	elseif (type(i) == 'number' and type(j) == 'string') then
		return table.concat{string.sub(str, 1, i - 1), j, string.sub(str, i + 1)}
	elseif (type(i) == 'number' and type(j) == 'nil') then
		return string.byte(str, i)
	end
	
	return string[i]
end



---Checks to see if the string starts with the given characters
function string.starts_with(str, chars)
	return chars == '' or string.sub(str, 1, string.len(chars)) == chars
end



---Checks to see if the string ends with the given characters
function string.ends_with(str, chars)
	return chars == '' or string.sub(str, -string.len(chars)) == chars
end



---Removes the length from the start of the string, returning the result
---Length can be a number or string
function string.remove_from_start(str, length)
	if (type(length) == 'number') then
		return string.sub(str, length + 1, string.len(str))
	elseif (type(length) == 'string') then
		return string.sub(str, string.len(length) + 1, string.len(str))
	else
		return str
	end
end



---Removes the length from the end of the string, returning the result
---Length can be a number or string
function string.remove_from_end(str, length)
	if (type(length) == 'number') then
		return string.sub(str, 1, string.len(str) - length)
	elseif (type(length) == 'string') then
		return string.sub(str, 1, string.len(str) - string.len(length))
	else
		return str
	end
end



---Removes a number of occurrences of the pattern from the string
---If limit is blank, removes all occurrences
function string.remove(str, pattern, limit)
	if (pattern == '' or pattern == nil) then
		return str
	end

	if (limit == '' or limit == nil) then
		str = string.gsub(str, pattern, '')
	else
		str = string.gsub(str, pattern, '', limit)
	end
	return str
end


--拼接字符串
function string.concat(str1, str2, concatStr)
	local ret =  table.concat({str1, str2}, concatStr)
	return ret
end



---Removes all occurrences of the pattern from the string
function string.remove_all(str, pattern)
	if (pattern == '' or pattern == nil) then
		return str
	end

	str = string.gsub(str, pattern, '')
	return str
end





---Removes the first occurrence of the pattern from the string
function string.remove_first(str, pattern)
	if (pattern == '' or pattern == nil) then
		return str
	end

	str = string.gsub(str, pattern, '', 1)
	return str
end



---Returns whether the string contains the pattern
function string.contains(str, pattern)
	if (pattern == '' or string.find(str, pattern, 1)) then
		return true
	end
	
	return false
end



---A case-insensitive string.find, returning start and end position of pattern in string
function string.findi(str, pattern)
	return string.find(string.lower(str), string.lower(pattern), 1)
end



---Returns the first substring which matches the pattern in the string from a start index
function string.find_pattern(str, pattern, start)
	if (pattern == '' or pattern == nil) then
		return ''
	end
	
	if (start == '' or start == nil) then
		start = 1
	end

	return string.sub(str, string.find(str, pattern, start))
end



------Split the string by the given pattern, returning an array of the result
------If pattern is omitted or nil, then default is to split on spaces
------Array index starts at 1
---function string.split(str, pattern)
---	local split = {}
---	local index = 1
	
---	if (pattern == '' or pattern == nil) then
---		pattern = '%s'
---	end
	
---	local previousstart = 1
---	local startpos, endpos = string.find(str, pattern, 1)
	
---	while (startpos ~= nil) do
---		split[index] = string.sub(str, previousstart, startpos - 1)
---		previousstart = endpos + 1
---		index = index + 1
---		startpos, endpos = string.find(str, pattern, endpos + 1)
---	end
	
---	split[index] = string.sub(str, previousstart, string.len(str))
	
---	return split
---end

---分割字字符串
--@usage split[index] = string.sub(str, previousstart, string.len(str))

--[[--
分割字字符串
]]
function string.split(str, delimiter)
	if (delimiter == '') then return false end
	local pos, arr = 0, {}
	-- for each divider found
	for st, sp in function() return string.find(str, delimiter, pos, true) end do
		table.insert(arr, string.sub(str, pos, st - 1))
		pos = sp + 1
	end
	table.insert(arr, string.sub(str, pos))
	return arr
end



---Returns the array of word contained within the string
---Array index starts at 1
function string.to_word_array(str)
	local words = {}
	local index = 1
	
	for word in string.gmatch(str, '%w+') do
		words[index] = word
		index = index + 1
	end
	
	return words
end



---Returns the number of letters within the string
function string.letter_count(str)
	local _, count = string.gsub(str, '%a', '')
	return count
end



---Returns the number of spaces within the string
function string.space_count(str)
	local _, count = string.gsub(str, '%s', '')
	return count
end



---Returns the number of times the pattern occurs within the string
function string.pattern_count(str, pattern)
	if (pattern == '' or pattern == nil) then
		return nil
	end

	local _, count = string.gsub(str, pattern, '')
	return count
end



---Returns a table of how many of each character appears in the string
---Table in the format: ["char"] = 2
function string.char_totals(str)
	local totals = {}
	local temp = ''
	
	for i = 1, string.len(str), 1 do
		temp = str[i]
		if (totals[temp]) then
			totals[temp] = totals[temp] + 1
		else
			totals[temp] = 1
		end
	end
	
	return totals
end

--模糊搜索,返回true、false,匹配单词中如果含有特殊字符串,返回false
function string.fuzzy_match(sourceStr,  searchStr)
	if string.find(searchStr,"[().%+-*?[^$]")then
		return 
	end 
	sourceStr = string.upper(sourceStr)
	searchStr = string.upper(searchStr)
	searchStr = string.gsub(searchStr,"",".*")
	if string.find(sourceStr,searchStr) then 
		return true
	end 
end

---Returns the number of words within the string
function string.word_count(str)
	local _, count = string.gsub(str, '%w+', '')
	return count
end



---Returns a string which contains the lengths of each each word found in the given string
function string.word_length(str)
	local lengths = string.gsub(str, '%w+', function(w) return string.len(w) end)
	return lengths
end



---Returns a table of how many of each word appears in the string
---Table in the format: ["word"] = 2
function string.word_totals(str)
	local totals = {}
	
	for word in string.gmatch(str, '%w+') do
		if (totals[word]) then
			totals[word] = totals[word] + 1
		else
			totals[word] = 1
		end
	end
	
	return totals
end



---Returns byte (unicode) representation of each character within the string as an array
---Array index starts at 1
function string.to_byte_array(str)
	local bytes = {}
	
	for i = 1, string.len(str), 1 do
		bytes[i] = string.byte(str, i)
	end
	
	return bytes
end



---Returns character representation of each character within the string as an array
---Array index starts at 1
function string.to_char_array(str)
	local chars = {}
	
	for i = 1, string.len(str), 1 do
		chars[i] = str[i]
	end
	
	return chars
end



---Returns a string where occurrences of the pattern are put into upper-case
function string.pattern_to_upper(str, pattern)
	if (pattern == '' or pattern == nil) then
		return str
	end

	local upper = string.gsub(str, pattern, string.upper)
	return upper
end



---Returns a string where occurrences of the pattern are put into lower-case
function string.pattern_to_lower(str, pattern)
	if (pattern == '' or pattern == nil) then
		return str
	end

	local lower = string.gsub(str, pattern, string.lower)
	return lower
end



---Returns a string, where the given string's occurrences of the pattern is replaced by
---the given characters, restricted by the given limit
function string.replace(str, pattern, chars, limit)
	if (pattern == '' or pattern == nil) then
		return str
	end

	if (limit == '' or limit == nil) then
		str = string.gsub(str, pattern, chars)
	else
		str = string.gsub(str, pattern, chars, limit)
	end
	return str
end



---Replaces the character at the given index with the given characters
function string.replace_at(str, index, chars)
	return table.concat{string.sub(str, 1, index - 1), chars, string.sub(str, index + 1)}
end



---Returns a string, where the given string's occurrences of the pattern is replaced by
---the given characters
function string.replace_all(str, pattern, chars)
	if (pattern == '' or pattern == nil) then
		return str
	end

	str = string.gsub(str, pattern, chars)
	return str
end



---Returns a string, where the given string's first occurrence of the pattern is replaced
---by the given characters
function string.replace_first(str, pattern, chars)
	if (pattern == '' or pattern == nil) then
		return str
	end

	str = string.gsub(str, pattern, chars, 1)
	return str
end



---Returns the index within the string for the first occurrence of the pattern after the
---given starting index
function string.index_of(str, pattern, start)
	if (pattern == '' or pattern == nil) then
		return nil
	end
	
	if (start == '' or start == nil) then
		start = 1
	end

	local position = string.find(str, pattern, start)
	return position
end



---Returns the index within the string for the first occurrence of the pattern
function string.first_index_of(str, pattern)
	if (pattern == '' or pattern == nil) then
		return nil
	end

	local position = string.find(str, pattern, 1)
	return position
end



---Returns the index within the string for the last occurrence of the pattern
function string.last_index_of(str, pattern)
	if (pattern == '' or pattern == nil) then
		return nil
	end
	
	local position = string.find(str, pattern, 1)
	local previous = nil
	
	while (position ~= nil) do
		previous = position
		position = string.find(str, pattern, previous + 1)
	end
	
	return previous
end



---Returns the character at the specified index in the string
function string.char_at(str, index)
	return str[index]
end



---Returns the byte (unicode) value of the character at given index in the string
---Basically the same as 'string.byte'
function string.byte_at(str, index)
	return string.byte(str, index)
end



---Returns the byte (unicode) value for the single given character
---nil is returned if not single character or otherwise
function string.byte_value(char)
	if (string.len(char) == 1) then
		return string.byte(char, 1)
	end
	
	return nil
end



---Compares two strings lexiographically. 1 is returned if str1 is greater than
---str2. -1 if str1 is less than str2. And 0 if they are equal
---This comparing is case-sensitive
function string.compare(str1, str2)
	local len1 = string.len(str1)
	local len2 = string.len(str2)
	local smallestLen = 0;
	
	if (len1 <= len2) then
		smallestLen = len1
	else
		smallestLen = len2
	end
	
	for i = 1, smallestLen, 1 do
		if (str1(i) > str2(i)) then
			return 1
		elseif (str1(i) < str2(i)) then
			return -1
		end
	end
	
	local lengthDiff = len1 - len2
	if (lengthDiff < 0) then
		return -1
	elseif (lengthDiff > 0) then
		return 1
	else
		return 0
	end
end



---Compares two strings lexiographically. 1 is returned if str1 is greater than
---str2. -1 if str1 is less than str2. And 0 if they are equal
---This comparing is case-insensitive
function string.comparei(str1, str2)
	return string.compare(string.lower(str1), string.lower(str2))
end



---Returns whether the two strings are equal to one another. True of they are,
---false otherwise
---This equals function is case-sensitive
function string.equal(str1, str2)
	local len1 = string.len(str1)
	local len2 = string.len(str2)
	
	if (len1 ~= len2) then
		return false
	end
	
	for i = 1, len1, 1 do
		if (str1[i] ~= str2[i]) then
			return false
		end
	end
	
	return true
end



---Returns whether the two strings are equal to one another. True of they are,
---false otherwise
---This equals function is case-insensitive
function string.equali(str1, str2)
	return string.equal(string.lower(str1), string.lower(str1))
end

---Returns the string representation of the given value. Be it either a
---number, boolean, string or a table. nil is returned otherwise for functions,
---threads, userdata and nil.
function string.valueof(value)
	local t = type(value)

	if (t == 'string') then
		return value
	elseif (t == 'number') then
		return '' .. value .. ''
	elseif (t == 'boolean') then
		if (value) then
			return "true"
		else
			return "false"
		end
	elseif (t == 'table') then
		local str = ""
		for k,v in pairs(value) do
			str = str .. "[" .. k .. "] = " .. v .. "\n"
		end
		str = string.sub(str, 1, string.len(str) - string.len("\n"))
		return str
	else
		return "nil"
	end
end



---Returns a string, where the given characters have been inserted into the
---string at the required index. An index of 0 specifies the front of the string
function string.insert(str, chars, index)
	if (index == 0) then
		return chars .. str
	elseif (index == string.len(str)) then
		return str .. chars
	else
		return string.sub(str, 1, index) .. chars .. string.sub(str, index + 1, string.len(str))
	end
end



---Returns a string, where the given characters have been inserted into the
---string rep times at the required index. An index of 0 specifies the front of
---the string
---For example:
--		string.insert_rep("ello", "h", 4, 0) = "hhhhello"
function string.insert_rep(str, chars, rep, index)
	local rep = string.rep(chars, rep)
	return string.insert(str, rep, index)
end



---Returns a string where all characters starting at the given index have
---been removed up to the end of the string (including the start index character)
function string.remove_to_end(str, index)
	if (index == 1) then
		return ""
	else
		return string.sub(str, 1, index - 1)
	end
end



---Returns a string where all char_aters starting at the given index have
---been removed down to the start of the string (including the start index character)
function string.remove_to_start(str, index)
	if (index == string.len(str)) then
		return ""
	else
		return string.sub(str, index + 1, string.len(str))
	end
end



---Returns a string where the given string has had any leading and
---trailing characters removed
---If char is left blank, then whitespaces are removed
--@usage
--string.trim("[[[word[[[", "%[") => "word"
--string.trim("   word   ") => "word"
function string.trim(str, char)
	if (char == '' or char == nil) then
		char = '%s'
	end

	local trimmed = string.gsub(str, '^' .. char .. '*(.-)' .. char .. '*$', '%1')
	return trimmed
end



---Returns a string where the given string has had any leading
---characters removed
---If char is left blank, then whitespaces are removed
function string.trim_start(str, char)
	if (char == '' or char == nil) then
		char = '%s'
	end

	local trimmed = string.gsub(str, '^' .. char .. '*', '')
	return trimmed
end



---Returns a string where the gievn string has had any trailing
---characters removed
---If char is left blank, then whitespaces are removed
function string.trim_end(str, char)
	if (char == '' or char == nil) then
		char = '%s'
	end

	local length = string.len(str)
	
	while (length > 0 and string.find(str, '^' .. char .. '', length)) do
		length = length - 1
	end
	
	return string.sub(str, 1, length)
end



---Returns a string where the given string has had variables substituted into it
--@usage
--string.subvar("x=$(x), y=$(y)", {x=200, y=300}) => "x=200, y=300"
--string.subvar("x=$(x), y=$(y)", {['x']=200, ['y']=300}) => "x=200, y=300"
function string.subvar(str, _table)
	str = string.gsub(str, "%$%(([%w_]+)%)", function(key)
		local value = _table[key]
		return value ~= nil and tostring(value)
	end)
	
	return str
end



---Rotates the string about the given index, returning the result.
--@usage
--string.rotate("hello, 3) => "lohel"
function string.rotate(str, index)
	local str1 = string.sub(str, 1, index)
	local str2 = string.sub(str, index + 1, string.len(str))
	return str2 .. str1
end



---Averages the two strings together. This is done by adding the byte (unicode) values
---of parallel characters and dividing by 2.
function string.average(str1, str2)
	local len1 = string.len(str1)
	local len2 = string.len(str2)
	local smallestLen = 0
	local newstr = ''
	
	if (len1 <= len2) then
		smallestLen = len1
	else
		smallestLen = len2
	end
	
	for i = 1, smallestLen, 1 do
		newstr = newstr .. string.char( (str1(i) + str2(i)) / 2 )
	end
	
	if (len1 <= len2) then
		newstr = newstr .. string.sub(str2, smallestLen + 1, string.len(str2))
	else
		newstr = newstr .. string.sub(str1, smallestLen + 1, string.len(str1))
	end
	
	return newstr
end



---Swaps the two characters at the given indices of the string
function string.swap(str, index1, index2)
	local temp = str[index1]
	str = str(index1, str[index2])
	return str(index2, temp)
end



---Sorts the string into ascending order according to their unicode values.
function string.sort_ascending(str)
	local chars = str:to_char_array()
	table.sort(chars, function(a,b) return a(1) < b(1) end)
	return table.concat(chars)
end



---Sorts the string into descending order according to their unicode values.
function string.sort_descending(str)
	local chars = str:to_char_array()
	table.sort(chars, function(a,b) return a(1) > b(1) end)
	return table.concat(chars)
end



---Returns the character with the highest byte (unicode) value
function string.highest(str)
	local s = string.sort_descending(str)
	return s[1]
end



---Returns the character with the lowest byte (unicode) value
function string.lowest(str)
	local s = string.sort_ascending(str)
	return s[1]
end



---Checks to see if the string is empty
function string.is_empty(str)
	if (str == '' or str == nil) then
		return true
	end
	
	return false
end



---Returns a table for the percentage of how much the string is formed of
---each word.
--@usage
--string.word_percents("hello, world!") = {"hello" = 38.46, "world" = 38.46}
function string.word_percents(str)
	local t = string.word_totals(str)
	local count = string.len(str)
	
	for k,v in pairs(t) do
		t[k] = ((string.len(k) * v) / count) * 100.0
	end
	
	return t
end



---Returns the percentage for how much of the string is formed by the given word
--@usage
--string.word_percent("hello, world!", "hello") = 50
function string.word_percent(str, word)
	local t = string.word_percents(str)
	
	if (t[word]) then
		return t[word]
	end
	
	return 0
end



---Returns a table for the percentage of how much the string is formed of
---each character.
--@usage
--string.char_percents("hello") = {"h" = 20, "e" = 20, "l" = 40, "o" = 20}
function string.char_percents(str)
	local t = string.char_totals(str)
	local count = string.len(str)
	
	for k,v in pairs(t) do
		t[k] = (v/count) * 100.0
	end
	
	return t
end



---Returns the percentage for how much of the string is formed by the given character
--@usage
--string.char_percent("hello", "h") = 20
function string.char_percent(str, char)
	local t = string.char_percents(str)
	
	if (t[char]) then
		return t[char]
	end
	
	return 0
end



---Returns the percentage for how much of the string is formed by whitespace
function string.space_percent(str)
	local count = string.space_count(str)
	return (count / string.len(str)) * 100.0
end



---Returns the number of uppercase characters in the string
function string.upper_count(str)
	local _, count = string.gsub(str, '%u', '')
	return count
end



---Returns the percentage for how much of the string is formed by uppercase
---characters
function string.upper_percent(str)
	local count = string.upper_count(str)
	return (count / string.len(str)) * 100.0
end



---Returns the number of lowercase characters in the string
function string.lower_count(str)
	local _, count = string.gsub(str, '%l', '')
	return count
end



---Returns the percentage for how much of the string is formed by lowercase
---characters
function string.lower_percent(str)
	local count = string.lower_count(str)
	return (count / string.len(str)) * 100.0
end



---Returns the number of single digits in the string
function string.digit_count(str)
	local _, count = string.gsub(str, '%d', '')
	return count
end



---Returns a table of how many of each single digit appears in the string
function string.digit_totals(str)
	local totals = {}
	
	for digit in string.gmatch(str, '%d') do
		if (totals[digit]) then
			totals[digit] = totals[digit] + 1
		else
			totals[digit] = 1
		end
	end
	
	return totals
end



---Returns a table for the percentage of how much the string is formed of
---each single digit.
--@usage
--string.digit_percents("hello, 2world!") = {"2" = 7.14}
function string.digit_percents(str)
	local t = string.digit_totals(str)
	local count = string.len(str)
	
	for k,v in pairs(t) do
		t[k] = ((string.len(k) * v) / count) * 100.0
	end
	
	return t
end



---Returns the percentage for how much of the string is formed by the given single digit
--@usage
--string.digit_percent("hello2", "2") = 16.67
function string.digit_percent(str, digit)
	local t = string.digit_percents(str)
	
	if (t[digit]) then
		return t[digit]
	end
	
	return 0
end



---Returns the amount of punctuation in the string
function string.punc_count(str)
	local _, count = string.gsub(str, '%p', '')
	return count
end



---Returns a table of how many of each punctuation appears in the string
function string.punc_totals(str)
	local totals = {}
	
	for punc in string.gmatch(str, '%p') do
		if (totals[punc]) then
			totals[punc] = totals[punc] + 1
		else
			totals[punc] = 1
		end
	end
	
	return totals
end



---Returns a table for the percentage of how much the string is formed of
---each punctuation.
--@usage
--string.punc_percents("hello, world!") = {"," = 7.69, "!" = 7.69}
function string.punc_percents(str)
	local t = string.punc_totals(str)
	local count = string.len(str)
	
	for k,v in pairs(t) do
		t[k] = ((string.len(k) * v) / count) * 100.0
	end
	
	return t
end



---Returns the percentage for how much of the string is formed by the given punctuation
--@usage
--string.punc_percent("hello, world!", ",") = 7.69
function string.punc_percent(str, punc)
	local t = string.punc_percents(str)
	
	if (t[punc]) then
		return t[punc]
	end
	
	return 0
end



---Concatenates an array of strings together, with optional seperation characters
---This is basically the same as doing table.concat(table, sep)
function string.join(array, sep)
	return table.concat(array, sep)
end



---Returns the Levenshtein distance between the two given strings
function string.levenshtein(str1, str2)
	local len1 = string.len(str1)
	local len2 = string.len(str2)
	local matrix = {}
	local cost = 0
	
	if (len1 == 0) then
		return len2
	elseif (len2 == 0) then
		return len1
	elseif (str1 == str2) then
		return 0
	end
	
	for i = 0, len1, 1 do
		matrix[i] = {}
		matrix[i][0] = i
	end
	for j = 0, len2, 1 do
		matrix[0][j] = j
	end
	
	for i = 1, len1, 1 do
		for j = 1, len2, 1 do
			if (str1[i] == str2[j]) then
				cost = 0
			else
				cost = 1
			end
			
			matrix[i][j] = math.min(matrix[i-1][j] + 1, matrix[i][j-1] + 1, matrix[i-1][j-1] + cost)
		end
	end
	
	return matrix[len1][len2]
end



---Makes the string's first character lowercase
function string.lower_first(str)
	return str(1, string.lower(str[1]))
end



---Makes the string's first character uppercase
function string.upper_first(str)
	return str(1, string.upper(str[1]))
end



---Randomly shuffles the given string
function string.shuffle(str)
	local temp = ''
	local length = string.len(str)
	local ran1, ran2 = 0, 0
	math.randomseed(os.time())
	
	for i = 1, length , 1 do
		ran1 = math.random(length)
		ran2 = math.random(length)
		temp = str[ran1]
		str = str(ran1, str[ran2])
		str = str(ran2, temp)
	end
	
	return str
end



---Converts the given integer value into a binary string of length limit
---If limit is omitted, then a binary string of length 8 is returned
function dectobin(dec, limit)
	if (limit == '' or limit == nil) then
		limit = 8
	end

	local bin = ''
	local rem = 0
	
	for i = 1, dec, 1 do
		rem = dec % 2
		dec = dec - rem
		bin = rem .. bin
		dec = dec / 2
		if (dec <= 0) then break end
	end
	
	local padding = limit - (string.len(bin) % limit)
	if (padding ~= limit) then
		bin = string.insert_rep(bin, '0', padding, 0)
	end
	
	return bin
end



---Returns the uuencoded representation of the given string
function string.uuencode(str)
	local padding = 3 - (string.len(str) % 3)
	if (padding ~= 3) then
		str = string.insert_rep(str, string.char(1), padding, string.len(str))
	end
	
	local uuenc = ''
	local bin1, bin2, bin3, binall = '', '', '', ''
	
	for i = 1, string.len(str) - 2, 3 do
		bin1 = dectobin(string.byte(str[i]), 8)
		bin2 = dectobin(string.byte(str[i+1]), 8)
		bin3 = dectobin(string.byte(str[i+2]), 8)
		
		binall = bin1 .. bin2 .. bin3

		uuenc = uuenc .. string.char(tonumber(binall(1,6), 2) + 32)
		uuenc = uuenc .. string.char(tonumber(binall(7,12), 2) + 32)
		uuenc = uuenc .. string.char(tonumber(binall(13,18), 2) + 32)
		uuenc = uuenc .. string.char(tonumber(binall(19,24), 2) + 32)
	end
	
	return uuenc
end



---Returns the actual string from a uuencoded string
function string.uudecode(str)	
	local padding = 4 - (string.len(str) % 4)
	if (padding ~= 4) then
		str = string.insert_rep(str, string.char(1), padding, string.len(str))
	end
	
	local uudec = ''
	local bin1, bin2, bin3, bin4, binall = '', '', '', '', ''
	
	for i = 1, string.len(str) - 3, 4 do
		bin1 = dectobin(string.byte(str[i]) - 32, 6)
		bin2 = dectobin(string.byte(str[i+1]) - 32, 6)
		bin3 = dectobin(string.byte(str[i+2]) - 32, 6)
		bin4 = dectobin(string.byte(str[i+3]) - 32, 6)
		
		binall = bin1 .. bin2 .. bin3 .. bin4
		
		uudec = uudec .. string.char(tonumber(binall(1,8), 2))
		uudec = uudec .. string.char(tonumber(binall(9,16), 2))
		uudec = uudec .. string.char(tonumber(binall(17,24), 2))
	end
	
	return string.trim(uudec, string.char(1))
end



---Returns a simple hash key for a string. If the check value is ommited
---then the string is hashed by the prime value of 17
---Best results occur when the check value is prime
function string.hash(str, check)
	local sum = 0
	local checksum = 17
	local length = string.len(str)
	
	if (check ~= '' and check ~= nil) then checksum = check end
	
	sum = str(1) + 1
	sum = sum + str(length) + length
	sum = sum + str(length/2) + math.ceil(length/2)
	
	return sum % checksum
end

---url字符转换
function string.urlencode_char(char)
	return "%" .. string.format("%02X", string.byte(char))
end

---url字符转换
function string.urlencode(str)
	---convert line endings
	str = string.gsub(tostring(str), "\n", "\r\n")
	---escape all characters but alphanumeric, '.' and '-'
	str = string.gsub(str, "([^%w%.%- ])", string.urlencode_char)
	---convert spaces to "+" symbols
	return string.gsub(str, " ", "+")
end

function string.ltrim(str)
    return string.gsub(str, "^[ \t\n\r]+", "")
end

function string.rtrim(str)
    return string.gsub(str, "[ \t\n\r]+$", "")
end

function string.trim(str)
    str = string.gsub(str, "^[ \t\n\r]+", "")
    return string.gsub(str, "[ \t\n\r]+$", "")
end

---检测手机号码是否正确
function string.is_phone_num( PhoneNumText )
    local phoneNum = string.trim(PhoneNumText);
    local start, length = string.find(phoneNum, "^1[3|4|5|8|7][0-9]%d+$"); ---判断手机号码是否正确
    if start ~= nil and length == 11 then
        return true ;
    end
    return false;
end

---计算文字utf8的长度
function string.utf8len(str)
	local len = #str
	local left = len
	local cnt = 0
	local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc }
	while left > 0 do
		local tmp = string.byte(str, -left)
		local i = #arr
		while arr[i] do
			if tmp >= arr[i] then
				left = left - i
				break
			end
			i = i - 1
		end
		cnt = cnt + 1
	end
	return cnt
end

--按照utf8格式取子串
function string.utf8_sub_str(str,subLen)

	if subLen == 0 then return "" end 
	if str == nil then 
		debug.traceback()
	print(str, "str")
	end

	local len = #str
	local left = len
	local cnt = 0
	local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc }
	while left > 0 do
		local tmp = string.byte(str, -left)
		local i = #arr
		while arr[i] do
			if tmp >= arr[i] then
				left = left - i
				break
			end
			i = i - 1
		end
		cnt = cnt + 1
		if cnt >= subLen then
			break;
		end
	end
	local temp = string.sub(str,0,len - left);
	return temp
end


function string.utf8_char_str( str, index )
 
	 local last = string.utf8_sub_str(str, index - 1)

	 local tem = string.utf8_sub_str(str, index) 
	 local utf8_char_str = string.sub(str, #last + 1, #tem)

	return utf8_char_str
end

-- local string = _G['string'];


-- for k,v in pairs(string) do
--     -- print_string(k)
--     -- print_string(v)
--     if string[k] then
--     	-- print("k---",k)
--     else
--     	string[k] = v;
--     	-- print("k2---",k)
--     end
-- end

return string;

================================================
FILE: lib/table.lua
================================================
--[[--table操作
@module table
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:45:20
]]

local tableConcat = table.concat
local tableInsert = table.insert

local type = type
local pairs = pairs
local tostring = tostring
local next = next
local TableLib = {}

--[[--
    计算表格包含的字段数量
    Lua table 的 "#" 操作只对依次排序的数值下标数组有效,TableLib.nums() 则计算 table 中所有不为 nil 的值的个数。

    @tparam table t 要计算的表格
    @return number 结果
    @usage
    local TableLib = import("bos.utils").TableLib
    TableLib.nums({a= 1,b=2})
]]
function TableLib.nums(t)
    local temp = checktable(t)
    local count = 0
    for k, v in pairs(temp) do
        count = count + 1
    end
    return count
end


--[[--
    将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值
    @tparam table dest 目标表格
    @tparam table src  来源表格
    @usage
    local dest = {a = 1, b = 2}
    local src  = {c = 3, d = 4}
    TableLib.merge(dest, src)
    -- dest = {a = 1, b = 2, c = 3, d = 4}
]]
function TableLib.merge(dest, src)
    if not src or not dest then
        return;
    end
    for k, v in pairs(src) do
        dest[k] = v
    end
end

--[[--
    合并两个表格的内容
    @tparam table src1 来源表格1
    @tparam table src2 来源表格2
    @return table 合并后的新表
    @usage
    local src1 = {a = 1, b = 2}
    local src2  = {c = 3, d = 4}
    local temp = TableLib.merge(src1, src2)
    -- src1 = {a = 1, b = 2}
    -- temp = {a = 1, b = 2, c = 3, d = 4}
]]
function TableLib.merge2(src1, src2)
    local tb ={}
    if src1 and next(src1) then
        for k, v in pairs(src1) do
            tableInsert(tb,v);
        end
    end
    if src2 and next(src2) then
        for k, v in pairs(src2) do
            tableInsert(tb,v);
        end
    end
    return tb;
end

--[[--
    合并两个表格的以数字开头的内容
    @tparam table src1 来源表格1
    @tparam table src2 来源表格2
    @return table 合并后的新表
    @usage
    local src1 = {a = 1, b = 2, 3}
    local src2  = {c = 3, d = 4, 4}
    local temp = TableLib.merge3(src1, src2)
     return {3, 4}
]]
function TableLib.merge3(src1, src2)
    local tb ={}
    if src1 and next(src1) then
        for k, v in pairs(src1) do
            if type(k) == "number" then
                tableInsert(tb,v);
            end
        end
    end
    if src2 and next(src2) then
        for k, v in pairs(src2) do
            if type(k) == "number" then
                tableInsert(tb,v);
            end
        end
    end
    return tb;
end

--[[--
    同步数据,把tab2 的数据同步到 tab1(不是合并)
    @tparam table tab1 来源表格1
    @tparam table tab2 来源表格2
    @usage
    local tab1 = {c = 1, b = 2,g=9}
    local tab2  = {c = 3, d = 4}
    TableLib.sync(tab1, tab2)
    -- tab1  = {c = 3, b = 2,g=9}
    -- tab2  = {c = 3, d = 4}
]]
function TableLib.sync(tab1, tab2)
    for k, v in pairs(tab2) do
        if tab1[k] ~= nil then
            tab1[k] = v;
        end
    end
end

--[[--
    从表格中查找指定值,返回其 key,如果没找到返回 nil
    @tparam table hashtable 表格
    @tparam mixed value 要查找的值
    @return string 该值对应的 key
    @usage
    local hashtable = {name = "dualface", comp = "chukong"}
    print(TableLib.key_of(hashtable, "chukong")) -- 输出 comp
]]
function TableLib.key_of(hashtable, value)
    for k, v in pairs(hashtable) do
        if v == value then return k end
    end
    return nil
end

--[[--
    从表格中查找指定值,返回其索引,如果没找到返回 false
    @tparam table array 表格
    @tparam mixed value 要查找的值
    @tparam number begin 起始索引值
    @return number 
    @usage
    local a = {"a","b","c"}
    TableLib.index_of(a,"b",1)
]]

function TableLib.index_of(array, value, begin)
    for i = begin or 1, #array do
        if array[i] == value then return i end
    end
    return false
end

--[[--
    从表格中删除指定值,返回删除的值的个数
    @tparam table array 表格
    @tparam mixed value 要删除的值
    @tparam boolean remove_all 是否删除所有相同的值
    @return number 删除的值的个数
    @usage 
    local array = {"a", "b", "c", "c"}
    print(TableLib.remove_by_value(array, "c", true)) -- 输出 2
]]
function TableLib.remove_by_value(array, value, remove_all)
    local c, i, max = 0, 1, #array
    while i <= max do
        if array[i] == value then
            table.remove(array, i)
            c = c + 1
            i = i - 1
            max = max - 1
            if not remove_all then break end
        end
        i = i + 1
    end
    return c
end

--[[--
    判断table是否为空
    @tparam table t 表格
    @return boolean 是否为空
    @usage
    TableLib.is_empty({}) -- true
]]
function TableLib.is_empty(t)
    if t and type(t)=="table" then --FIXME 此句可以判空,为何还要循环表内元素?
        return next(t)==nil;
    end
    return true;
end

--[[--
    判断table是否为table
    @tparam table t 表格
    @return boolean 是否为table
    @usage
    TableLib.is_table({}) -- true
]]
function TableLib.is_table(t)
    if type(t)=="table" then
        return true;
    end
    return false;
end

--[[--
    复制table
    @tparam table st 表格
    @return table 复制后的新表
    @usage
    TableLib.copy_tab({1,2,3,4,5}) 
]]
function TableLib.copy_tab(st)
    local tab = {}
    for k, v in pairs(st or {}) do
        if type(v) ~= "table" then
            tab[k] = v
        else
            tab[k] = TableLib.copy_tab(v)
        end
    end
    return tab
end

--[[--
    从table1复制到table2
    @tparam table target 目标表格
    @tparam table source 被复制表格
    @usage
    TableLib.copy_to({1,2,3,4,5},{"c","c","c"}) 
]]

function TableLib.copy_to(target, source)
    for _,v in ipairs(source or {}) do
        table.insert(target, v)
    end
end


--[[--
    table校验,返回自身或者{}
    @tparam table t 目标表格
    @return table 返回自身或者{}
    @usage
    TableLib.checktable({1,2,3,4,5}) 
]]
function TableLib.checktable(t)
    return TableLib.verify(t)
end

--[[--
    table校验,返回自身或者{}
    @tparam table t 目标表格
    @return table 返回自身或者{}
    @usage
    TableLib.verify({1,2,3,4,5}) 
]]
function TableLib.verify(t)
    if t and type(t)=="table" then
        return t;
    end
    return {};
end

--[[--
    获取table 元素数量
    @tparam table t 目标表格
    @return number 数量
    @usage
    TableLib.size({1,2,3,4,5}) 
]]
function TableLib.size(t)
    if type(t) ~= "table" then
        return 0;
    end

    local count = 0;
    for _,v in pairs(t) do
        count = count + 1;
    end

    return count;
end

--[[--
    比较两个table的内容是否相同
    @tparam table t1 目标表格
    @tparam table t2 目标表格
    @return boolean 是否相同
    @usage
    TableLib.equal({1,2,3,4,5},{5,4,3,2,1}) 
]]
function TableLib.equal(t1,t2)
    if type(t1) ~= type(t2) then
        return false;
    else
        if type(t1) ~= "table" then
            return t1 == t2;
        else
            local len1 = TableLib.size(t1);
            local len2 = TableLib.size(t2);
            if len1 ~= len2 then
                return false;
            else
                local isEqual = true;
                for k,v in pairs(t1) do
                    if t2[k]  == nil then
                        isEqual = false;
                        break;
                    else
                        if type(t2[k]) ~= type(v) then
                            isEqual = false;
                            break;
                        else
                            if type(v) ~= "table" then
                                if t2[k] ~= v then
                                    isEqual = false;
                                    break;
                                end
                            else
                                isEqual = TableLib.equal(v,t2[k]);
                                if not isEqual then
                                    break;
                                end
                            end
                        end
                    end
                end

                return isEqual;
            end
        end
    end
end

--[[--
    从表里获取n个随机值
    @tparam table t 目标表格
    @tparam number num 获取个数
    @return table 获取的值的table
    @usage
    TableLib.random({1,2,3,4,5},2) 
]]
function TableLib.random(t, num)
    assert(type(t) == "table", "invalid arg");
    local randomList = { }

    if not num or num > #t then
        num = #t;
    end

    local rangeList = { };
    for i,v in ipairs(t) do
        rangeList[i] = v;
    end

    for i = 1, num do
        local index = math.random(i, #rangeList);--生成一个随机数
        rangeList[i], rangeList[index] = rangeList[index], rangeList[i];--交换
        randomList[i] = rangeList[i];--交换以后把i位置的牌放到要返回的函数中
    end

    return randomList;
end

--[[--
    序列化table
    @tparam table root 目标表格
    @return string 结果
    @usage
    TableLib.tostring({1,2,3,4,5}) 
]]
function TableLib.tostring(root)
    if not root then return end
    local cache = {  [root] = "root" }
    local flag = {};
    local function _dump(t,name)
        local mt = getmetatable(t)
        if mt and mt.__tostring then
            return tostring(t)
        end
        local temp = {}
        for i,v in ipairs(t) do
            flag[i] = true;
            if cache[v] then
                tableInsert(temp, cache[v])
            elseif type(v) == "table" then
                cache[v] = string.format("%s[%d]", name, i)
                tableInsert(temp, string.format("%s", _dump(v, cache[v])))
            else
                tableInsert(temp, tostring(v))
            end
        end
        for k,v in pairs(t) do
            if not flag[k] then
                local key = tostring(k)
                if cache[v] then
                    tableInsert(temp, string.format("%s=%s", key, cache[v]))
                elseif type(v) == "table" then
                    cache[v] = string.format("%s.%s", name, key)
                    tableInsert(temp, string.format("%s=%s", key, _dump(v, cache[v])))
                else
                    tableInsert(temp, string.format("%s=%s", key, tostring(v)))
                end
            end
        end
        return string.format("{%s}", tableConcat(temp,","));
    end
    return _dump(root, "root");
end

--[[--
    合并多个表到src
    @tparam table src 目标表格
    @tparam tables ... 待合并表,可多个
    @usage
    TableLib.deep_merge({1,2,3,4,5},{"c","c","s","c"},{a = "ccc"}) 
]]
function TableLib.deep_merge( src, ... )
    local arg = {...};
    for i,v1 in ipairs(arg) do
        for k,v in pairs(v1) do
            if type(v) == "table" and type(src[k]) == "table" then
                TableLib.deep_merge(src[k], v);
            else
                src[k] = v;
            end
        end
    end
end

--[[--
    遍历表,处理函数返回true终止
    @tparam table t 目标表格
    @tparam function func 处理函数
    @usage
    local function a(i,v)
        -- return true 返回true时停止遍历
    end
     TableLib.select({1,2,3,4}, a)
]]
function TableLib.select(t, func)
    for i,v in ipairs(t) do
        if func and func(i,v) == true then
            return i, v;
        end
    end
end

--[[--
    遍历所有元素,返回所有处理函数return true的元素
    @tparam table t 目标表格
    @tparam function func 处理函数
    @return table array所有处理函数return true的元素
    @usage
    local function a(i,v)
        -- return true 返回true时最后要返回该元素
    end
     TableLib.select({1,2,3,4}, a)
]]
function TableLib.select_all(t, func)
    local temp = {};
    for i,v in ipairs(t) do
        if func and func(i,v) == true then
            temp[#temp+1] = v;
        end
    end
    return temp;
end

--[[--
    检索某个元素
    @tparam table t 目标表格
    @tparam mixed ...  键值索引,传入多个会递归检索
    @return nil|mixed 返回键值对应的值
    @usage
    local a = {
        b = {  -- 传入"b"作为参数时就返回该表
            c = 1  -- 在"b"之后传入"c",就返回1
        }
    }
     TableLib.retrive(a,"b","c") -- return 1
]]
function TableLib.retrive(t, ...)
    if not t then
        return
    end
    local arg = {...}
    local tmp = t;
    for _,v in ipairs( arg ) do
        if tmp[v] then
            tmp = tmp[v];
        else
            return;
        end
    end
    return tmp;
end

--[[--
    设置table为只读
    @tparam table t 目标表格
    @usage
    local a = {1,1,1,1}
    TableLib.lock_write(a)
]]
function TableLib.lock_write(t)
    local mt = getmetatable(t) or {};
    mt.__newindex = function(_,k,v)
        error(string.format("can't write [%s] into table",k))
    end;
    if not getmetatable(t) then
        setmetatable(t, mt);
    end
end

--[[--
    取消设置table为只读
    @tparam table t 目标表格
    @usage
    local a = {1,1,1,1}
    TableLib.lock_write(a)
    TableLib.release_lock_write(a)
]]
function TableLib.release_lock_write(t)
    local mt = getmetatable(t);
    if not (mt and mt.__newindex) then
        return
    end
    mt.__newindex = nil
end

--[[--
    通过连续下标,获取table子集
    @tparam table t 目标表格,array
    @tparam number from 起始下标
    @tparam number to 截止下标
    @return table 子集
    @usage
    local a = {1,1,1,1}
    TableLib.get_subset(a,2,3)
]]
function TableLib.get_subset(t, from, to)
    assert(from > 0 and from <= to and to <= #t, string.format("invalid range : %d, %d", from, to));
    local sub = {}
    for i=from,to do
        sub[#sub + 1] = t[i]
    end
    return sub
end

--[[--
    克隆一份表数据
    @tparam table object 目标表格
    @return table 克隆表
    @usage
    local a = {1,1,1,1}
    TableLib.clone(a)
]]
function TableLib.clone(object)
    local lookup_table = {}
    local function _copy(object)
        if type(object) ~= "table" then
            return object
        elseif lookup_table[object] then
            return lookup_table[object]
        end
        local new_table = {}
        lookup_table[object] = new_table
        for key, value in pairs(object) do
            new_table[_copy(key)] = _copy(value)
        end
        return setmetatable(new_table, getmetatable(object))
    end
    return _copy(object)
end

local function infilter( filter, key, value )
    if filter.key then
        for i,v in ipairs(filter.key) do
            if v == key then return i end
        end
    end
    if filter.class then
        for i,v in ipairs(filter.class) do
            if typeof and typeof(value, v) then return i end
        end
    end
    return false
end

--[[--
    克隆一份表数据,可过滤
    @tparam table object 目标表格
    @tparam table filter 过滤器{key = {"ingorekey1",...},class = {class1,...} }
    @return table 克隆表
    @usage
    local a = {1,1,1,1}
    local filter = {
        key = {"1","3"} -- 键值1、3对应的值不克隆
    }
    TableLib.clone(a,filter)
]]
function TableLib.clone2(object, filter)
    local lookup_table = {}
    filter = filter or {}
    local function _copy(object, filter)
        if type(object) ~= "table" then
            return object
        elseif lookup_table[object] then
            return lookup_table[object]
        end
        local new_table = {}
        lookup_table[object] = new_table
        for key, value in pairs(object) do
            if not infilter(filter, key, value) then
                new_table[_copy(key, filter)] = _copy(value, filter)
            end
        end
        return setmetatable(new_table, getmetatable(object))
    end
    return _copy(object, filter)
end

--[[--
    加载string为table
    @tparam string strTab 定义table的字符串
    @return table 定义的表
    @usage
    local a = "{1,1,1,1}"
    TableLib.load_str_tab(a)
]]
function TableLib.load_str_tab(strTab)
    if string.match(strTab, "^%s*%{.*%}%s*$") then
        return loadstring("return " .. strTab)()
    end
    return strTab;
end

--[[--
    返回table中所有key的集合
    @tparam table tab 表
    @return table key表
    @usage
    local a = "{1,1,1,1}"
    TableLib.all_keys(a)
]]
function TableLib.all_keys( tab )
    if next(tab) == nil then
        print("nilTab")
        return
    end

    local keys = {}
    for k,_ in pairs(tab) do
        table.insert(keys, k)
    end
    return keys
end

return TableLib;

================================================
FILE: lib/time.lua
================================================
--[[--时间操作
@module time
@author iwiniwin

Date   2020-01-16 13:27:14
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:44:56
]]
local time = {}

-- time.sleep = function ( num )
--     -- print("hhhh")
--     -- os.execute("sleep 1000")
--     io.popen("sleep 10000")
-- end
-- print(os.clock())

-- time.sleep(500000)
-- print("你好")

-- 单位是秒
-- require("socket")
-- time.sleep = function ( second )
--     socket.select(nil, nil, second)
-- end

-- time.sleep = function ( second )
--     if second > 0 then
--         os.execute("ping -n " .. second + 1 .. " localhost > NUL")
--     end
-- end


-- time.sleep()

-- print("uuuuuu")


return time

================================================
FILE: mvc/loader.lua
================================================
--[[--
模块加载器
@module Loader
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:58:34
]]
local ModuleConfig = import("mvc.module_config")

-- 根据配置读取模块
local config = {}
for k,v in pairs(ModuleConfig) do
   table.insert(config, {key = k, value = v})
end
table.sort(config, function ( a, b )
    -- 根据initOrder排序,确定加载顺序
    if a.value.initOrder and not b.value.initOrder then
        return true
    end
    if a.value.initOrder and b.value.initOrder then
        return a.value.initOrder < b.value.initOrder
    end
    return false
end)

-- 模块创建函数
local function create_module( name, params )
    params = params or {}
    assert(params.file)
    local viewClass = require(params.file)
    local view = new(viewClass)
    view._tag = {name = name, params = params}
    return view
end

-- 根据配置加载模块
local moduleList = {}
for i,v in ipairs(config) do
    if moduleList[v.key] then
        error("模块已经存在:" .. v.key)
    end
    local mod = create_module(v.key, v.value)
    moduleList[v.key] = mod
end

return moduleList

================================================
FILE: mvc/module1/test1_ctr.lua
================================================
--[[--Test1Ctr 示例Ctr
@module Test1Ctr
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:44:50
]]
local Test1Ctr = class()
Test1Ctr._class_name = "Test1Ctr"

function Test1Ctr:ctor( delegate )
    dump("load Test1Ctr")
    self.m_delegate = delegate
end

function Test1Ctr:get_ui(  )
    return self.m_delegate
end

-- 刷新视图
function Test1Ctr:update_view( data )
    -- Ctr负责逻辑处理,转换视图可识别的数据
    -- data = process(data)
    
    -- 由View负责刷新视图
    local ui = self:get_ui();
    if ui then
        ui:update_view(data)
    end
end

function Test1Ctr:dtor( ... )
    dump("unload Test1Ctr")
    self.m_delegate = nil
end

return Test1Ctr;

================================================
FILE: mvc/module1/test1_view.lua
================================================
--[[--ldoc desc
Test1View 示例View
@module Test1View
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 14:00:03
]]
local Test1Ctr = import("mvc.module1.test1_ctr")
local Test1View = class()
Test1View._class_name = "Test1View"

function Test1View:ctor( ... )
    dump("load Test1View")
    self:bind_ctr()
end

function Test1View:bind_ctr(  )
    if self.mCtr then
        return false
    else
        self.mCtr = new(Test1Ctr, self)
        return true
    end
end

function Test1View:get_ctr( ... )
    return self.mCtr
end

-- 更新视图
function Test1View:update_view( data )
    -- 数据驱动
    -- 视图与逻辑分离,数据有什么就更新什么
    if data.title then
        -- 更新title
    end
    if data.content then
        -- 更新content
    end
    if data.other then
        -- 更新other
    end
end

function Test1View:unbind_ctr( ... )
    if self.mCtr then
        delete(self.mCtr)
        self.mCtr = nil
    end
end

function Test1View:dtor( ... )
    dump("unload Test1View")
    self:unbind_ctr()
end

return Test1View;

================================================
FILE: mvc/module2/test2_ctr.lua
================================================
--[[--ldoc desc
Test2Ctr 示例Ctr
@module Test2Ctr
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:37:37
]]
local Test2Ctr = class()
Test2Ctr._class_name = "Test2Ctr"

function Test2Ctr:ctor( delegate )
    dump("load Test2Ctr")
    self.m_delegate = delegate
end

function Test2Ctr:getUI(  )
    return self.m_delegate
end

-- 刷新视图
function Test2Ctr:update_view( data )
    -- Ctr负责逻辑处理,转换视图可识别的数据
    -- data = process(data)
    
    -- 由View负责刷新视图
    local ui = self:getUI();
    if ui then
        ui:update_view(data)
    end
end

function Test2Ctr:dtor( ... )
    dump("unload Test2Ctr")
    self.m_delegate = nil
end

return Test2Ctr;

================================================
FILE: mvc/module2/test2_view.lua
================================================
--[[--ldoc desc
Test2View 示例View
@module Test2View
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:59:57
]]
local Test2Ctr = import("mvc.module2.test2_ctr")
local Test2View = class()
Test2View._class_name = "Test2View"

function Test2View:ctor( ... )
    dump("load Test2View")
    self:bind_ctr()
end

function Test2View:bind_ctr(  )
    if self.mCtr then
        return false
    else
        self.mCtr = new(Test2Ctr, self)
        return true
    end
end

function Test2View:get_ctr( ... )
    return self.mCtr
end

-- 更新视图
function Test2View:update_view( data )
    -- 数据驱动
    -- 视图与逻辑分离,数据有什么就更新什么
    if data.title then
        -- 更新title
    end
    if data.content then
        -- 更新content
    end
    if data.other then
        -- 更新other
    end
end

function Test2View:unbind_ctr( ... )
    if self.mCtr then
        delete(self.mCtr)
        self.mCtr = nil
    end
end

function Test2View:dtor( ... )
    dump("unload Test2View")
    self:unbind_ctr()
end

return Test2View;

================================================
FILE: mvc/module_config.lua
================================================
--[[--
ModuleConfig
@module ModuleConfig
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:59:10
]]
local ModuleConfig = {}

ModuleConfig.Module1 = {
    file = "mvc.module1.test1_view",
    initOrder = 2,
}

ModuleConfig.Module2 = {
    file = "mvc.module2.test2_view",
    initOrder = 1,
}

return ModuleConfig

================================================
FILE: pattern/AbstractFactoryPattern.lua
================================================
--[[--
抽象工厂模式
@module AbstractFactoryPattern
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:39:53
]]
package.path = package.path .. ";..\\?.lua;"
require("_load")


--[[
    抽象工厂模式
    定义:
        抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
    优点:
        1. 抽象工厂允许客户使用抽象的接口来创建一组相关的产品,
        而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦
    设计原则:
        1. 依赖倒置原则,依赖抽象,而不依赖具体类
]]

-- 抽象工厂,披萨原料工厂,定义一组接口用于生产产品家族。原料的获取采用了抽象工厂模式
local PizzaIngredientFactory = class();
-- 创建面团接口
function PizzaIngredientFactory:createDough( ... )
    -- body
end
-- 创建酱料接口
function PizzaIngredientFactory:createSauce( ... )
    -- body
end

-- 具体工厂类1
local NYPizzaIngredientFactory = class();
-- 实现创建面团接口
function NYPizzaIngredientFactory:createDough( ... )
    dump("create NY Ingredient Factory dough")
end
-- 实现创建酱料接口
function NYPizzaIngredientFactory:createSauce( ... )
    dump("create NY Ingredient Factory sauce")
end

-- 具体工厂类2
local ChicagoPizzaIngredientFactory = class();
-- 实现创建面团接口
function ChicagoPizzaIngredientFactory:createDough( ... )
    dump("create Chicago Ingredient Factory dough")
end
-- 实现创建酱料接口
function ChicagoPizzaIngredientFactory:createSauce( ... )
    dump("create Chicago Ingredient Factory sauce")
end

-- 抽象披萨类
local Pizza = class();
function Pizza:prepare( ... )
    -- body
end

-- 具体披萨类
local CheesePizza = class();
-- 接收PizzaIngredientFactory对象
function CheesePizza:ctor( factory )
    self.factory = factory
end
-- 实现prepare接口
function CheesePizza:prepare( ... )
    local dough = self.factory:createDough();
    local sauce = self.factory:createSauce();
end

local PizzaStore = class();

function PizzaStore:orderPizza( type )
    local pizza = self:createPizza(type);  -- 调用子类的创建披萨方法
    pizza:prepare()
    -- pizza:bake()
    -- pizza:cut()
    -- pizza:box()
    return pizza;
end

-- 声明一个抽象的工厂方法,由子类去实现。pizza的获取采用了工厂方法模式
function PizzaStore:createPizza( type )
    
end

local NYStyleCheesePizza = class();
-- 第一个子类,纽约披萨店
local NYPizzaStore = class(PizzaStore)

function NYPizzaStore:createPizza( type )
    local factory = new(NYPizzaIngredientFactory);  -- 使用具体的原料工厂
    if type == "cheese" then
        return new(CheesePizza, factory);
    end
end


local ChicagoCheesePizza = class();
-- 第二个子类,芝加哥披萨店
local ChicagoStore = class(PizzaStore)

function ChicagoStore:createPizza( type )
    local factory = new(ChicagoPizzaIngredientFactory);  -- 使用具体的原料工厂
    if type == "cheese" then
        return new(CheesePizza, factory)
    end
end

-------------- 测试 -------------- 

local store = new(ChicagoStore)
store:orderPizza("cheese")

-- create Chicago Ingredient Factory dough
-- create Chicago Ingredient Factory sauce

================================================
FILE: pattern/AdapterPattern.lua
================================================
--[[--
适配器模式
@module AdapterPattern
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:39:53
]]
package.path = package.path .. ";..\\?.lua;"
require("_load")

--[[
    适配器模式
    定义:
        将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不相容的类可以合作无间
    优点:
        可以通过创建适配器进行接口转换,让不兼容的接口编程兼容。这可以让客户从实现的接口解耦
        如果在一段时间以后,我们想改变接口,适配器可以将改变的部分封装起来,客户就不必为了应对不同的系统而每次跟着修改
]]

-- 鸭子类
local Duck = class();  
-- 鸭子有呱呱叫能力
function Duck:quack( ... )
    -- body
end
-- 鸭子有飞行能力
function Duck:fly( ... )
    -- body
end

-- 绿头鸭
local MallardDuck = class(Duck);
function MallardDuck:quack( ... )
    dump("mallard duck quack")
end
function MallardDuck:fly( ... )
    dump("mallard duck fly")
end

-- 火鸡类
local Turkey = class();
-- 火鸡有咯咯叫能力
function Turkey:gobble( ... )
    -- body
end
-- 火鸡有飞行能力
function Turkey:fly( ... )
    -- body
end

-- 野生火鸡
local WildTurkey = class(Turkey);
function WildTurkey:gobble( ... )
    dump("wild turkey gobble")
end
function WildTurkey:fly( ... )
    dump("wild turkey fly")
end

-- 适配器,让火鸡来冒充鸭子
local TurkeyAdapter = class(Duck);  -- 适配器需要实现想转换成的类型接口,也就是客户所期望看到的接口。即quack和fly
function TurkeyAdapter:ctor( turkey )
    self.turkey = turkey
end
function TurkeyAdapter:quack( ... )
    self.turkey:gobble();
end
-- 火鸡有飞行能力
function TurkeyAdapter:fly( ... )
    self.turkey:fly();
end


-------------- 测试 -------------- 

local turkey = new(WildTurkey);
local turkeyAdapter = new(TurkeyAdapter, turkey);  -- 将火鸡包装进一个火鸡适配器,使它看起来像是一只鸭子

-- 测试鸭子
local duck = turkeyAdapter;
duck:quack();  -- wild turkey gobble
duck:fly();  -- wild turkey fly

================================================
FILE: pattern/CORPattern.lua
================================================
--[[--
责任链模式
@module CORPattern
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:39:53
]]
package.path = package.path .. ";..\\?.lua;"
require("_load")
--[[
    责任链模式
    定义:
        将接收者对象连成一条链,并在该链上传递请求,直到有一个接收者对象处理它
        通过让更多对象有机会处理请求,避免了请求发送者和接收者之间的耦合
    使用场景:
        经常被用在窗口系统中,处理鼠标和键盘之类的事件
    优点:
        1. 将请求的发送者和接收者解耦
        2. 通过改变链内的成员或调动它们的次序,允许动态地新增或者删除责任
    缺点:
        1. 内存消耗,链上的所有对象都需要创建,可能有些对象根本不会被用到(或很少走进满足该对象处理的条件)
        2. 性能消耗,处理需要一层层的传递,才能被正确的对象所处理
]]


--[[
    责任链模式实例
    客户到售楼处买房,请求折扣的例子
]]
-- 价格处理类
local PriceHandler = class()

function PriceHandler:ctor( ... )
    -- 抽象方法 processDiscount
    assert(self.processDiscount, "子类必须实现processDiscount接口")
end

function PriceHandler:setSuccessor( successor )
    self.successor = successor
end

-- 目前是三个Handler,sale销售 和 manager经理 和 ceo

local Sale = class(PriceHandler)
Sale._class_name = "Sale"

function Sale:processDiscount( discount )
    if discount < 0.2 then
        print("sale处理了" .. discount .. "的折扣")
    else
        self.successor:processDiscount(discount)
    end
end

local Manager = class(PriceHandler)
Manager._class_name = "Manager"

function Manager:processDiscount( discount )
    if discount < 0.4 then
        print("manager处理了" .. discount .. "的折扣")
    else
        self.successor:processDiscount(discount)
    end
end

local CEO = class(PriceHandler)

function CEO:processDiscount( discount )
    if discount < 0.8 then
        print("ceo处理了" .. discount .. "的折扣")
    else
        print("ceo拒绝了" .. discount .. "的折扣")
    end
end

-- PriceHandler工厂类
-- 添加一个工厂类提供createPriceHandler方法,
-- 而不直接在PriceHandler中提供的原因是基于单一职责原则,
-- PriceHandler见名知意是用于价格处理的,而不应该有提供PriceHandler的功能
local PriceHandlerFactor = class()

function PriceHandlerFactor.createPriceHandler( ... )
    -- 构造责任链
    local sale = new(Sale)
    local manager = new(Manager)
    local ceo = new(CEO)
    -- 销售设置后继是经理
    sale:setSuccessor(manager)
    -- 经理的后继是ceo
    manager:setSuccessor(ceo)
    -- ceo不存在直接后继

    -- 由sale优先处理
    return sale
end


-- 顾客
local Customer = class()

function Customer:setPriceHandler( priceHandler )
    self.priceHandler = priceHandler
end

function Customer:requestDiscount( discount )
    self.priceHandler:processDiscount(discount)
end


-- 测试
local customer = new(Customer)

customer:setPriceHandler(PriceHandlerFactor.createPriceHandler())

for i = 1, 10 do 
    -- 100次折扣申请
    customer:requestDiscount(math.random())
end

-- 如何应对变化
-- 如果此时ceo希望添加一个vp的角色,帮他审核0.5以下的折扣
-- 只需要添加一个vp类,同时修改以下工厂方法
print("加入一个vp角色")

local VP = class(PriceHandler)

function VP:processDiscount( discount )
    if discount < 0.6 then
        print("vp处理了" .. discount .. "的折扣")
    else
        self.successor:processDiscount(discount)
    end
end

function PriceHandlerFactor.createPriceHandler( ... )
    local sale = new(Sale)
    local manager = new(Manager)
    local ceo = new(CEO)
    -- 添加一个vp
    local vp = new(VP)
    -- 销售设置后继是经理
    sale:setSuccessor(manager)

    -- 修改经理的后继为vp
    manager:setSuccessor(vp)

    -- vp的后继为ceo
    vp:setSuccessor(ceo)
    -- ceo不存在直接后继

    -- 由sale优先处理
    return sale
end


-- 测试
local customer = new(Customer)

customer:setPriceHandler(PriceHandlerFactor.createPriceHandler())

for i = 1, 10 do 
    -- 100次折扣申请
    customer:requestDiscount(math.random())
end




================================================
FILE: pattern/CompositePattern.lua
================================================
--[[--
组合模式
@module CompositePattern
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:39:53
]]
package.path = package.path .. ";..\\?.lua;"
require("_load")

--[[
    适配器模式
    定义:
        将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不相容的类可以合作无间
    优点:
        组合以单一责任设计原则换取透明性。什么是透明性?通过让组件的接口同时包含一些管理子节点和叶子节点的操作,
        客户就可以将组合和叶子节点一视同仁。也就是说,一个元素究竟是组合还是叶子节点,对客户是透明的
]]

-- 利用组合模式来设计菜单

-- 菜单组件提供了一组接口,让菜单和菜单项共同使用
local MenuComponent = class();
function MenuComponent:getName( ... )
end
function MenuComponent:getPrice( ... )
end
function MenuComponent:add( component )
end
function MenuComponent:remove( component )
end
function MenuComponent:getChild( index )
end
function MenuComponent:print(  )
end


-- 菜单(组合菜单)。覆盖一些菜单组件对它有用的方法。此组合类可以持有菜单项和其它菜单
local Menu = class(MenuComponent);
function Menu:ctor( name )
    self.name = name
    self.menuComponents = {}  -- 菜单下可以有更多组件
end
function Menu:getName( ... )
end
function Menu:add( component )
    table.insert(self.menuComponents, component)
end
function Menu:remove( component )
    table.remove(self.menuComponents, component)
end
function Menu:getChild( index )
    return self.menuComponents[index]
end
function Menu:print(  )
    dump("menu : name is " .. self.name)
    for i,v in ipairs(self.menuComponents) do
        v:print();
    end
end

-- 菜单项。也覆盖一些对它有意义的方法。没有意义的就置之不理。因为菜单项已经是叶子节点,所以它的下面不能有任何组件
local MenuItem = class(MenuComponent);
function MenuItem:ctor( name, price )
    self.name = name
    self.price = price
end
function MenuItem:getName( ... )
end
function MenuItem:getPrice( ... )
end
function MenuItem:print( ... )
    dump("menu item : name is " .. self.name .. ", price is " .. self.price)
end

-------------- 测试 -------------- 

local pancakeHouseMenu = new(Menu, "PANCAKE HOUSE MENU");  -- 煎饼屋菜单
local dinerMenu = new(Menu, "DINER MENU");  -- 餐厅菜单
local cafeMenu = new(Menu, "CAFE MENU");  -- 咖啡菜单

local allMenus = new(Menu, "ALL MENUS")
allMenus:add(pancakeHouseMenu);
allMenus:add(dinerMenu)
allMenus:add(cafeMenu)

dinerMenu:add(new(MenuItem, "Pasta", 3.89));  -- 加入菜单项,面团

local dessertMenu = new(Menu, "DESSERT MENU");  -- 甜点菜单
dessertMenu:add(new(MenuItem, "Apple Pie", 1.59));  -- 加入菜单项

dinerMenu:add(dessertMenu);  -- 加入菜单,甜点菜单(甜点菜单属于餐厅菜单的子菜单)

allMenus:print()

--[[
menu : name is ALL MENUS
menu : name is PANCAKE HOUSE MENU"
menu : name is DINER MENU"
menu item : name is Pasta, price is 3.89"
menu : name is DESSERT MENU"
menu item : name is Apple Pie, price is 1.59"
menu : name is CAFE MENU"
]]

================================================
FILE: pattern/FactoryMethodPattern.lua
================================================
--[[--
工厂方法模式
@module FactoryMethodPattern
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:39:53
]]
package.path = package.path .. ";..\\?.lua;"
require("_load")


--[[
    简单工厂模式,不是一个真正的模式,但经常被用于封装创建对象的代码
]]

local CheesePizza = class();  -- 芝士披萨
local PepperoniPizza = class();  -- 意大利香肠披萨

local SimplePizzaFactory = class() 

-- 简单工厂,根据传入的参数,决定创建出哪一种产品类的实例
SimplePizzaFactory.createPizza = function ( type )
    if type == "cheese" then
        return new(CheesePizza) 
    elseif type == "pepperoni" then
        return new(PepperoniPizza)
    end
end

--[[
    工厂方法模式
    定义:
        定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。
        工厂方法让类把实例化推迟到子类
    优点:
        1. 通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的
    设计原则:
        1. 依赖倒置原则,依赖抽象,而不依赖具体类
]]

local PizzaStore = class();

function PizzaStore:orderPizza( type )
    local pizza = self:createPizza(type);  -- 调用子类的创建披萨方法
    -- pizza:prepare()
    -- pizza:bake()
    -- pizza:cut()
    -- pizza:box()
    return pizza;
end

-- 声明一个抽象的工厂方法,由子类去实现。实例化的责任被移到一个方法中,此方法就如同是一个工厂
function PizzaStore:createPizza( type )
    -- body
end

local NYStyleCheesePizza = class();
-- 第一个子类,纽约披萨店
local NYPizzaStore = class(PizzaStore)

function NYPizzaStore:createPizza( type )
    if type == "cheese" then
        dump("create ny cheese pizza")
        return new(NYStyleChesePizza);
    end
end


local ChicagoCheesePizza = class();
-- 第二个子类,芝加哥披萨店
local ChicagoStore = class(PizzaStore)

function ChicagoStore:createPizza( type )
    if type == "cheese" then
        dump("create chicago cheese pizza")
        return new(ChicagoCheesePizza)
    end
end

-------------- 测试 -------------- 

local store = new(ChicagoStore)
store:orderPizza("cheese")  -- create chicago cheese pizza

================================================
FILE: pattern/ObserverPattern.lua
================================================
--[[--
观察者模式
@module ObserverPattern
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:40:08
]]
package.path = package.path .. ";..\\?.lua;"
require("_load")
--[[
    观察者模式
    定义:
        定义了对象之间的一对多依赖,这样一来,
        当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
    优点:
        1. 主题和观察者之间是松耦合,主题只知道观察者实现了观察者接口,不需要知道
            观察者的具体是谁,做了些什么
        2. 任何时候都可以增加或删除观察者,主题不会受到任何影响
    设计原则:
        1. 为了交互对象之间的松耦合设计而努力
        松耦合的设计能让我们建立有弹性的OO系统,能够应对变化,因为 对象之间的依赖降到了最低
        2. 找出程序会变化的方面,然后将其和固定不变的方面相分离
        3. 针对接口编程,不针对实现编程
        观察者利用主题的接口注册,主题利用观察者的接口通知观察者
        4. 多用组合,少用继承
        观察者模式利用组合将许多观察者组合进主题中
    注意:
        有多个观察者时,不可以依赖特定的通知顺序
]]


--[[
    观察者模式原型
]]
-- 主题 出版者
local Subject = class()

function Subject:ctor( ... )
    -- 观察者队列
    self.observers = {}
end

-- 注册观察者
function Subject:registerObserver( observer )
    table.insert(self.observers, observer)
end

-- 移除观察者
function Subject:removeObserver( observer )
    for i,v in ipairs(self.observers) do
        if v == observer then
            table.remove(self.observers, i)
        end
    end
end

-- 通知所有的观察者对象
function Subject:notifyObservers( ... )
    -- ...参数可以是自己,拉模型,观察者通过自己这个对象去获取更新的信息
    -- 也可以是具体的状态信息,推模型(推荐这个)
    for i,v in ipairs(self.observers) do
        v:update( ... )
    end
end

-- 观察者 订阅者
local Observer = class() -- 纯接口
-- 所有观察者必须实现观察接口
function Observer:ctor( ... )
    -- 定义了update接口
    assert(self.update, "必选实现update方法")
end

--[[
    观察者模式实例
    小明和小红订阅天气信息
]]

-- 天气(主题)
local Weather = class(Subject)

-- 扩展内容 
-- 添加changed标志 java.util.Observer中有
function Weather:setChanged( ... )
    self.changed = true
end

function Weather:clearChanged( ... )
    self.changed = false
end

function Weather:hasChanged( ... )
    return self.changed
end

-- 发布天气信息
-- 利用changed的好处,使更新观察者时有更多的弹性
-- 如果没有changed则一旦天气信息有了变化就会通知观察者,太过明锐
-- 而通过changed,可以在天气变化达到某个条件时,再调用setChanged()进行有效的更新
function Weather:setWeatcherInfo( ... )
    if self.changed then
        self:notifyObservers( ... )
    end
    self.changed = false
end

-- 订阅天气的人(观察者)
local People = class(Observer)

function People:ctor( name )
    self.name = name
end

function People:update( ... )
    print(string.format("%s收到了天气信息:%s", self.name, ...))
end

-- 测试
local weather = new(Weather)

-- object.lua new实现有问题 这两个的name都是小红
local ming = new(People, "小明")
local hong = new(People, "小红")

weather:registerObserver(ming)
weather:registerObserver(hong)

-- 发布天气信息
-- 设置changed通知观察者
weather:setChanged()
weather:setWeatcherInfo("晴朗")

-- 移除一个观察者
weather:removeObserver(ming)

-- 没有设置changed,不会通知观察者
print(weather:hasChanged(), "change状态")
weather:setWeatcherInfo("阴天")



================================================
FILE: pattern/ProxyPattern.lua
================================================
--[[--
@module ProxyPattern
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:40:27
]]


================================================
FILE: pattern/StrategyPattern.lua
================================================
--[[--策略模式
@module StrategyPattern
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:40:54
]]
package.path = package.path .. ";..\\?.lua;"
require("_load")
--[[
    策略模式
    定义:
        将可变的部分从程序中抽象分离成算法接口
        在该接口下分别封装一系列算法实现,并使他们可以相互替换
        从而导致客户端程序独立于算法的改变
    优点:
        1. 足够灵活,不同的策略只需要给出封装的接口的不同实现即可,富有弹性,可以较好地应对变化
        2. 复用代码,更易于维护,可以复用相同的策略
        3. 消除大量的条件语句
    缺点:
        1. 增加了对象的数目
        2. 客户代码需要了解策略的具体细节
    设计原则:
        1. 找出应用中需要变化的部分,把他们独立出来,不要和那些不需要变化的代码混在一起
        鸭子的飞行行为是千变万化的,但是鸭子具有飞行行为是不变的,
        将这个不变的部分抽象为飞行策略接口,而具体的飞行行为交给子类去实现
        2. 面向接口编程,而不是面向实现编程
        不如鸭子超类只是持有了飞行策略接口,而不是具体的飞行实现
        3. 多用组合,少用继承
]]


--[[
    策略模式原型
]]
local Super = class()
function Super:ctor( ... )
    -- body
end

function Super:setStrategy( strategy )
    -- 通过组合注入策略
    self.strategy = strategy
end

function Super:interface( ... )
    -- 通过策略实现某个功能
    self.strategy:interface()
end

-- 策略接口
local Strategy = class()
function Strategy:ctor( ... )
    -- 声明了某个策略接口
    assert(self.interface, "必须实现某策略")
end


--[[
    策略模式实例
    有一个鸭子的父类,已经有一个正常鸭子的实现,后面需要再实现橡皮鸭(不会飞),太空鸭(坐火箭飞)
]]

-- 鸭子超类
local Duck = class()
-- 鸭子都有一个外观
function Duck:display( ... )

end

function Duck:setFlyStrategy( flyStrategy )
    self.flyStrategy = flyStrategy
end

function Duck:fly( ... )
    self.flyStrategy:fly()
end

-- 飞行策略接口
local FlyStrategy = class()
function FlyStrategy:ctor( ... )
    assert(self.fly, "必须实现飞行接口")
end


-- 具体飞行策略

-- 振翅高飞
local FlyWithWin = class(FlyStrategy)
function FlyWithWin:fly( ... )
    print("振翅高飞")
end
local flyWithWin = new(FlyWithWin)


-- 正常鸭
local NormalDuck = class(Duck)
function NormalDuck:ctor( ... )
    self:setFlyStrategy(flyWithWin)
end
function NormalDuck:display( ... )
    print("我是正常鸭")
end

-- 测试
local normalDuck = new(NormalDuck)
normalDuck:display()
normalDuck:fly()


-- 不会飞(也是一种飞行策略)
local FlyNoWay = class(FlyStrategy)
function FlyWithWin:fly( ... )
    print("不会飞")
end
local flyNoWay = new(FlyNoWay)

-- 橡皮鸭
local RubberDuck = class(Duck)
function RubberDuck:ctor( ... )
    self:setFlyStrategy(flyNoWay)
end
function RubberDuck:display( ... )
    print("我是橡皮鸭")
end

-- 测试
local rubberDuck = new(RubberDuck)
rubberDuck:display()
rubberDuck:fly()


-- 坐火箭飞
local FlyWithRocket = class(FlyStrategy)
function FlyWithRocket:fly( ... )
    print("坐火箭飞")
end
local flyWithRocket = new(FlyWithRocket)

-- 太空鸭
local SpaceDuck = class(Duck)
function SpaceDuck:ctor( ... )
    self:setFlyStrategy(flyWithRocket)
end
function SpaceDuck:display( ... )
    print("我是正常鸭")
end

-- 测试
local spaceDuck = new(SpaceDuck)
spaceDuck:display()
spaceDuck:fly()

-- 对于鸭子超类,没有在其中直接定义fly方法,或者fly接口的原因
-- 不直接定义fly方法,不是所有的鸭子都会飞,对于不会飞的鸭子需要覆盖该方法,但是可能会由于某些原因忘记覆盖
-- 不直接定义fly接口,这样的话,所有的子类都必须要实现该接口,
-- 而且代码无法复用,重复代码多,比如同一个飞行策略,在具有相同的策略的子类中都要写一遍

-- 采用策略模式的好处,灵活不同的策略可以有不同的实现,当某些子类有共同的飞行策略,还可以直接复用该策略



================================================
FILE: pattern/TemplatePattern.lua
================================================
--[[--模板方法模式
@module TemplatePattern
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:41:10
]]
package.path = package.path .. ";..\\?.lua;"
require("_load")
--[[
    模板方法模式
    定义:

        在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。
        模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤
    优点:
        1. 对算法有更多的控制权,超类主导一切,拥有且保护这个算法
        2. 超类的存在将代码的复用最大化,算法只存在超类中,容易修改
        3. 模板方法提供了一个框架,可以让各种子类插进来,不同的子类实现自己的方法就可以
    设计原则:
        好莱坞原则:别调用我们,我们会调用你
        允许底层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些底层组件
]]


--[[
    模板方法模式原型
]]
local SuperClass = class()

function SuperClass:templateMethod( ... )
    -- 模板方法 子类不应该覆盖它

    assert(self.primitiveOperation1, "子类必须实现primitiveOperation1")
    assert(self.primitiveOperation2, "子类必须实现primitiveOperation2")

    self.primitiveOperation1()
    self.primitiveOperation1()

    self.concreteOperation()

    self.hook()
end

function SuperClass:concreteOperation( ... )
    -- 在超类中具体实现,子类不应该覆盖
    -- 可以被模板方法直接使用,或者被子类使用
end

function SuperClass:hook( ... )
    -- 一个具体方法,但什么也不做
    -- 钩子方法,子类可以根据情况决定要不要覆盖他
    -- 如果算法的这个部分是可选的,就用钩子
    -- 钩子可以让子类能够有机会对模板方法中某些即将发生的步骤做出反应
end

--[[
    模板方法模式实例
    封装一个制作饮品的具体算法
]]

local Drink = class()

function Drink:prepareDrink( ... )

    assert(self.brew)
    assert(self.addCondiments)

    -- 煮沸水
    self.boilWater()
    -- 冲泡
    self.brew()
    -- 倒入杯中
    self.pourInCap()
    -- 添加调料
    if self:wantsCondiments() then
        self.addCondiments()
    end
end

function Drink:boilWater( ... )
    print("把水煮沸。。。")
end

function Drink:pourInCap( ... )
    print("倒入杯中。。。")
end

function Drink:wantsCondiments( ... )
    -- 钩子方法
    return true
end
-- 子类

local Coffee = class(Drink)

function Coffee:brew( ... )
    print("用沸水冲泡咖啡粉")
end

function Coffee:addCondiments( ... )
    print("添加牛奶和糖")
end


local coffee = new(Coffee)
coffee:prepareDrink()

local Tea = class(Drink)

function Tea:brew( ... )
    print("用沸水浸泡茶叶")
end

function Tea:addCondiments( ... )
    print("添加柠檬")
end

local tea = new(Tea)
tea:prepareDrink()

-- 不加调料的茶

local TeaNoCondiments = class(Drink)

function TeaNoCondiments:brew( ... )
    print("用沸水浸泡茶叶")
end

function TeaNoCondiments:addCondiments( ... )
    print("添加柠檬")
end

function TeaNoCondiments:wantsCondiments( ... )
    return false
end

local tea = new(Tea)
tea:prepareDrink()

================================================
FILE: test.lua
================================================
--[[--
LuaKit测试用例
@module test
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-04-01 16:59:33
]]

-- 测试面向对象
local function test_oop( ... )
    local Class1 = class()
    function Class1:ctor( ... )
        dump("Class1:ctor")
    end
    function Class1:dtor( ... )
        dump("Class1:dtor")
    end

    -- Class2集成于Class1
    local Class2 = class(Class1)
    function Class2:ctor( ... )
        dump("Class2:ctor")
    end
    function Class2:dtor( ... )
        dump("Class2:dtor")
    end
    -- 实例化对象
    local c1 = new(Class1)
    local c2 = new (Class2)
    -- 销毁对象
    delete(c1)
    delete(c2)

end

-- 测试dump
local function test_dump()
    local data = {
        key1 = 34,
        key2 = "str",
        key3 = {
            key4 = {
                key5 = 56
            },
            key6 = 78
        }
    }
    dump(data, "this is a dump test")
end

-- 测试分模块加载
local function test_load_module( ... )
    local moduleList = import("mvc.loader")

    -- 卸载模块
    for k,v in pairs(moduleList) do
        delete(v)
    end
end

-- 测试性能分析
local function test_profile( ... )
    local new_profiler = import("utils.profiler")
    local profiler = new_profiler("call")
    profiler:start()  -- 开启性能分析

    local function aaa(  )
        for i = 1, 10000000 do

        end
    end
    local function ttt(  )
        aaa()
    end
    ttt()

    -- 同时支持分析协程内的函数调用情况
    local co = coroutine.create(function ( ... )
        aaa()
    end)
    coroutine.resume(co)

    profiler:stop()  -- 停止性能分析
    -- 输出分析结果到文件
    profiler:dump_report_to_file("profile.txt")
end

-- 测试内存泄漏检测工具
local function test_memory_monitor( ... )
    local MemoryMonitor = import("utils.memory_monitor")
    local memoryMonitor = new(MemoryMonitor)

    a = {}
    function test( ... )
        local b = {xxx = "xxx"}
        a.b = b
        memoryMonitor:add_to_leak_monitor(b, "b")  --将b添加到内存检测工具,此时a没有被释放掉 则b也释放不掉
    end
    test()

    -- 由于a在引用b,因此b存在内存泄漏
    memoryMonitor:update()

    -- a不再引用b,b也被释放
    a = nil
    memoryMonitor:update()  -- 没有内存泄漏,这里不会打印日志
end

-- 测试组件系统
local function test_component( ... )
    local ComponentBase = import("core.component.component_base")
    local ComponentExtend = import("core.component.component_extend")

    local A = class()
    ComponentExtend(A)

    -- 组件1
    local Component1 = class(ComponentBase)
    Component1.exportInterface = {
        {"test1"},
    }
    function Component1:test1( ... )
        dump("call test1 ...")
    end

    -- 组件2
    local Component2 = class(ComponentBase)
    Component2.exportInterface = {
        {"test2"},
    }
    function Component2:test2( ... )
        dump("call test2 ...")
    end

    local a = new(A)

    a:bind_component(Component1)  -- 对象a绑定组件1 拥有test1方法
    a:bind_component(Component2)  -- 对象a绑定组件2 拥有test2方法
    a:test1()
    a:test2()
    
    a:unbind_component(Component1)  -- 解绑组件1 丧失test1方法
    -- a:test1()  -- 报错 attempt to call method 'test1' (a nil value)
end

-- 测试事件分发系统
local function test_event_system( ... )
    local EventSystem = new(import("core.event.event_system"))
    local Event = import("core.event.event")

    -- 简单用法
    EventSystem:on("test", function ( ... )
        dump({...})
    end)

    EventSystem:emit("test", "param1", "param2")

    -- 高级用法
    local A = class()
    function A:on_key_down( key )
        dump(key, "key name A")
    end
    EventSystem:on(Event.KeyDown, A.on_key_down, {target = A})

    local B = class()
    function B:on_key_down( key )
        dump(key, "key name B")

        return true  -- 可以中断事件派发
    end

    -- 后注册的事件通过提高优先级可以保证先被调用
    EventSystem:on(Event.KeyDown, B.on_key_down, {target = B, priority = 2})

    EventSystem:emit(Event.KeyDown, "Ctrl")

    EventSystem:off_all(B)  -- 通过target取消注册

    EventSystem:emit(Event.KeyDown, "Ctrl")

end

-- test_oop()
-- test_dump()
-- test_load_module()
-- test_profile()
-- test_memory_monitor()
-- test_component()
test_event_system()

-- 数据观察追踪 回退系统


================================================
FILE: utils/dump.lua
================================================
--[[--
格式化输出table(格式化过程中,排序操作会比较耗时)
@module dump
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:41:51
]]

local isSort  = true;

local table_format = string.format
local string_len = string.len
local string_rep = string.rep

local math_randomseed = math.randomseed
local debug_traceback = debug.traceback

local function _dump_value(v)
    if type(v) == "string" then
        v = "\"" .. v .. "\""
    end
    return tostring(v)
end

--- dump 输出table的内容
--@param value 输出的table
--@string desciption 调试信息格式
--@int nesting 输出时的嵌套层级,默认为 15
--@usage local t = {key = "xxx"}
--dump(t)
local function dump(value, desciption, nesting)
    if type(nesting) ~= "number" then nesting = 10 end

    local lookup = {}
    local result = {}
    local traceback = string.split(debug_traceback("", 2), "\n")
    
    local str = "- dump from: " .. string.trim(traceback[3]);
    local function _dump(value, desciption, indent, nest, keylen)
        desciption = desciption or "<var>"
        local spc = ""
        if type(keylen) == "number" then
            spc = string_rep(" ", keylen - string_len(_dump_value(desciption)))
        end
        if type(value) ~= "table" then
            result[#result +1 ] = table_format("%s%s%s = %s", indent, _dump_value(desciption), spc, _dump_value(value))
        elseif lookup[tostring(value)] then
            result[#result +1 ] = table_format("%s%s%s = *REF*", indent, _dump_value(desciption), spc)
        else
            lookup[tostring(value)] = true
            if nest > nesting then
                result[#result +1 ] = table_format("%s%s = *MAX NESTING*", indent, _dump_value(desciption))
            else
                result[#result +1 ] = table_format("%s%s = {", indent, _dump_value(desciption))
                local indent2 = indent.."    "
                local keys = {}
                local keylen = 0
                local values = {}
                for k, v in pairs(value) do
                    if k~="___message" then
                        keys[#keys + 1] = k
                        local vk = _dump_value(k)
                        local vkl = string_len(vk)
                        if vkl > keylen then keylen = vkl end
                        values[k] = v
                    end
                end
                if isSort == true then
                    table.sort(keys, function(a, b)
                        if type(a) == "number" and type(b) == "number" then
                            return a < b
                        else
                            return tostring(a) < tostring(b)
                        end
                    end)
                end


                for i, k in ipairs(keys) do
                    _dump(values[k], k, indent2, nest + 1, keylen)
                end
                result[#result +1] = table_format("%s}", indent)
            end
        end
    end
    _dump(value, desciption, "- ", 1)

    str = str .. "\n" .. table.concat(result, "\n")
    print(str)
end

return dump;

================================================
FILE: utils/dump_to_file.lua
================================================
--[[--
序列化lua table
@module dump_to_file
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:42:17
]]

local M = {};

local table_format = string.format
local string_len = string.len
local string_rep = string.rep


---数列化table 报错到文件
--@string t 表
--@string tabName 表名
--@string path 保存目录,可选
--@retrurn tableStr
function M.serialize(t, tabName,path)
    local function dump(value, desciption, nesting)
    	local lookup = {}
    	local result = {}
    	if type(nesting) ~= "number" then nesting = 100 end

		local function _dump_value(v)
		    if type(v) == "string" then
		        v = string.format("%q", v)
		    end
		    if type(v) == "function" or type(v) == "userdata" then
		    	v = string.format("%q", tostring(v))
		    end

		 	-- if type(v) == "userdata" then
		  --   	v = string.format("%q", tostring(v))
		  --   end

		    -- if type(v) == "number" then
		    	-- v = string.format("%.2f",v)
		    	-- 如果是小数,保留小数点后两位
		    	-- if math.floor(v) < v then
		    	-- 	v = string.format("%.2f",v)
		    	-- end
		    -- end
		    return tostring(v)
		end

		local function _dump_key(v)
		    if type(v) == "number" then
		        v = "[" .. v .. "]"
		    end
		    return v
		end

	    local function _dump(value, desciption, indent, nest, keylen)
	        desciption = desciption or "<var>"
	        local spc = ""
	        if type(keylen) == "number" then
	            spc = string_rep(" ", keylen - string_len(_dump_value(desciption)))
	        end

	        if type(value) ~= "table" then
	            result[#result +1 ] = table_format("%s%s%s = %s,", indent, _dump_key(desciption), spc, _dump_value(value))
	        elseif lookup[tostring(value)] then
	            result[#result +1 ] = table_format("%s%s%s = '*REF*%s',", indent, desciption, spc,tostring(value))
	        else
	            lookup[tostring(value)] = true
	            if nest > nesting then
	                result[#result +1 ] = table_format("%s%s = '*MAX NESTING*',", indent, desciption)
	            else
	            	result[#result +1 ] = table_format("%s%s = {", indent, _dump_key(desciption))
	                local indent2 = indent.."    "
	                local keys = {}
	                local keylen = 0
	                local values = {}
	                for k, v in pairs(value) do
	                	if k~="___message" then
	                		keys[#keys + 1] = k
		                    local vk = _dump_value(k)
		                    local vkl = string_len(vk)
		                    if vkl > keylen then keylen = vkl end
		                    values[k] = v
	                	end
	                end
	                -- table.sort(keys, function(a, b)
	                --     if type(a) == "number" and type(b) == "number" then
	                --         return a < b
	                --     else
	                --         return tostring(a) < tostring(b)
	                --     end
	                -- end)
	                for i, k in ipairs(keys) do
	                    _dump(values[k], k, indent2, nest + 1, keylen)
	                end
	                result[#result +1] = table_format("%s},", indent)
	            end
	        end
	    end

    	_dump(value, desciption, "", 1)
    	result[1] 		= "{";
    	result[#result] = "}";

    	local ret = ""
	    for i, line in ipairs(result) do
	        ret = ret .. line .. "\n";
	    end
	    return ret;
	end

	tabName = tabName or "ret"
    

    local str = "do local " .. tabName .. " =\n"..dump(t) .. string.format("\nreturn %s end",tabName);
    -- local path = System.getStorageTempPath()  .. tabName .. ".lua"
    local filePath = tabName .. ".lua"
    if path then
        filePath = path .. "/" .. filePath
    end
    M.writefile(str,filePath)
    return str;
end


function M.writefile(str, file)
    --os.remove(file);
    local file =io.open(file,"w");
    if file then
    	file:write(str);
    	file:close();
    end
end


function M.dump_to_file(t,tabName,path)
	return M.serialize(t,tabName,path)
end


return M;

================================================
FILE: utils/memory_monitor.lua
================================================
--[[--
lua内存泄漏检测工具
@module MemoryMonitor
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:42:34
]]
--[[
    lua内存泄漏检测工具
    原理:弱表中的引用是弱引用,不会导致对象的引用计数发生变化
    即如果一个对象只有弱引用指向它,那么gc会自动回收该对象的内存
]]
--[[
    Lua运行了一个垃圾收集器来收集所有死对象来完成自动内存管理的工作
    Lua实现了一个增量标记-扫描收集器。它使用间歇率和步进倍率这两个数字来控制垃圾收集循环,都是以百分数为单位(例如:值100在内部表示1)

    间歇率控制收集器在开启新的循环前要等待多久,增大这个值将减少收集器的积极性。当这个值比100小的时候,收集器在开始新的循环前不会有等待,
    设置这个值为200就会让收集器等到总内存使用量达到之前的两倍时才开始新的循环

    步进倍率控制着收集器运行速度相对于内存分配速度的倍率,增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。不要把这个值设置
    的小于100,那样的话收集器就工作的太慢了以至于永远都干不完一个循环。默认值是200,表示收集器以内存分配的两倍速工作

    collectgarbage("collect") :  做一次完整的垃圾收集循环
    collectgarbage("count") : 以k字节数为单位返回Lua使用的总内存数,这个值有小数部分,所以只需要乘上1024就能得到Lua使用的准确字节数
    collectgarbage("restart") : 重启垃圾收集器的自动运行
    collectgarbage("setpause") : 将arg设为收集器的间歇率,并返回间歇率的前一个值
    collectgarbage("setstepmul") : 将arg设为收集器的步进倍率,并返回步进倍率的前一个值
    collectgarbage("step") : 单步运行垃圾收集器,步长大小由arg控制,传入0时,收集器步进一步,传入非0值,收集器收集相当于Lua分配这么多()内存的工作。如果垃圾收集器结束一个循环将返回true
    collectgarbage("stop") : 停止垃圾收集器的运行。在调用重启前,收集器只会因显示的调用运行
]]

-- 监控间隔配置(单位:秒)
local MonitorConfig = {
    -- 内存泄漏监控间隔
    memLeakInterval = 1,
}

local MemoryMonitor = {}

function MemoryMonitor:ctor( ... )
    -- 内存泄漏弱引用表
    self.__memLeakTable = {}
    -- mode字段可以取 k, v, kv 分别表示table中的 key, value,是弱引用的, kv就是二者的组合
    -- 对于一个table,任何情况下,只要它的key或者value中的一个被gc,那么这个key-value pair就从表中移除了
    setmetatable(self.__memLeakTable, {__mode = "kv"})
    -- 内存泄漏监控器
    self.__memLeakMonitor = nil

    self:start()
end

-- 开始检测
function MemoryMonitor:start( ... )
    self.__memLeakMonitor = self:__mem_leak_monitoring()
end


--[[
把一个表或者对象添加到内存检测工具中,如果该表或者对象不存在外部引用,则说明释放干净
否则内存泄漏工具会输出日志
@table t 观察的对象 表
@string tName 表的别名

@usage 
local memoryMonitor = new(MemoryMonitor)
memoryMonitor:add_to_leak_monitor(self, "xx模块")
]]
function MemoryMonitor:add_to_leak_monitor( t, tName )
    if not self.__memLeakMonitor then
        return
    end

    assert("string" == type(tName), "invalid params")

    -- 必须以名字+地址的方式作为键值
    -- 内存泄漏经常是一句代码多次分配出内存而忘了回收,因此tName经常是相同的
    local name = string.format("%s@%s", tName, tostring(t))
    if nil == self.__memLeakTable[name] then
        self.__memLeakTable[name] = t
    end
end

-- 更新弱表信息
function MemoryMonitor:update( dt )
    dt = dt or 10
    if self.__memLeakMonitor then
        self.__memLeakMonitor(dt)
    end
end



function MemoryMonitor:__mem_leak_monitoring( ... )
    local monitorTime = MonitorConfig.memLeakInterval
    local interval = MonitorConfig.memLeakInterval
    local str = nil
    return function( dt )
        interval = interval + dt
        if interval >= monitorTime then
            interval = interval - monitorTime

            -- 强制调用gc
            collectgarbage("collect")
            collectgarbage("collect")
            collectgarbage("collect")
            collectgarbage("collect")

            local flag = false
            -- 打印当前内存泄漏监控表中依然存在(没有被释放)的对象信息
            str = "存在以下内存泄漏:"
            for k,v in pairs(self.__memLeakTable) do
                str = str .. string.format("    \n%s = %s", tostring(k), tostring(v))
                flag = true
            end
            str = str .. "\n请仔细检查代码!!!"
            if flag then
                print(str)
            end
        end
    end
end

return MemoryMonitor

--[[
-- TODO 待研究情况
print("--------------分隔符--------------")
a = {}

local b = {xxx = "xxx"}

a.b = b

memoryMonitor:add_to_leak_monitor(b, "b")

a = nil
-- 此时b仍然没有被释放掉
-- 可能是由于b是local变量,不仅有a在引用b,可能lua的堆栈也在对其引用,导致无法被释放。
-- 可以对比上面在函数里定义b的区别
memoryMonitor:update()
--]]


================================================
FILE: utils/profiler.lua
================================================
--[[--
lua性能分析工具
@module profiler
@author iwiniwin

Date   2019-11-15 19:20:39
Last Modified by   iwiniwin
Last Modified time 2020-01-16 13:45:50
]]
--[[
    debug.getinfo(level, arg) : 返回一个包含函数信息的table
    level表示函数调用的层级,表示要输出哪个函数的信息
    arg是一个字符串,其中每个字符代表一组字段,用于指定希望获取那些信息,可以是"n","S","I","u","f","L"中的一个或组合
    n : 表示name(函数名)和namewhat(函数类型,field, upvalue, global)
    S : 表示source(函数所属文件名), linedefined(函数定义起始行号), lastlinedefined(函数定义结束行号), what(函数类型,Lua, C, main), short_src(函数所属文件名,source的短版本)
    l : 表示currentline(上级函数被调用的行号)
    u : 表示nups(函数的upvalue值的个数)
    f : 表示func(函数本身)
    L : 表示activelines(一个包含行号的table,可理解为该函数运行的代码的行号)
    debug.sethook(hook, mask, count) : 将一个函数作为钩子函数设入。字符串mask以及数字count决定了钩子将在何时调用
    掩码是由下列字符组合成的字符串
    "c" : 每当lua调用一个函数时,调用钩子
    "r" : 每当lua从一个函数内返回时,调用钩子
    "l" : 每当lua进入新的一行时,调用钩子
    当count值大于0的时候,每执行完count数量的指令后就会触发钩子

]]
-- package.path = package.path .. ";..\\?.lua;"
-- require("_load")
local EMPTY_TIME                       = "0.0000"       -- Detect empty time, replace with tag below
local emptyToThis                      = "~"

local timeWidth                        = 7
local relaWidth                        = 6
local callWidth                        = 10

local divider = "";
local formatOutput                     = "";
local formatFunTime                    = "%04.4f"
local formatFunRelative                = "%03.1f"
local formatFunCount                   = "%"..(callWidth-1).."i"
local formatHeader                     = ""
local scale                            = 1;

local function charRepetition(n, character)
    local s   = {}
    character = character or " "
    for _ = 1, n do
        table.insert(s,character)
    end
    return table.concat(s)
end

local Profiler = {}

--[[
创建一个性能分析工具对象
@string variant 性能分析模式 "call" or "time"
@usage
local profiler = new_profiler("call")
profiler:start()
-- do something
profiler:stop()
profiler:dump_report_to_file("profile.txt")
]]
local function new_profiler( variant )
    if Profiler.running then
        print("Profiler already running")
        return
    end

    variant = variant or "time"

    if variant ~= "time" and variant ~= "call" then
        print("Profiler method must be 'time' or 'call'")
        return
    end

    local newprof = {}
    for k,v in pairs(Profiler) do
        newprof[k] = v
    end
    newprof.variant = variant
    return newprof
end

--[[
启动性能分析,核心是利用debug.sethook对函数调用进行钩子
每次只能启动一个
]]
function Profiler:start( ... )
    if Profiler.running then
        return
    end
    Profiler.running = self

    self.caller_cache = {}
    self.callstack = {}

    self.start_time = os.clock()
    if self.variant == "time" then

    elseif self.variant == "call" then 
        -- 因为垃圾回收会导致性能分析下降严重,所以先放缓垃圾回收
        self.setpause = collectgarbage("setpause")
        self.setstepmul = collectgarbage("setstepmul")
        collectgarbage("setpause", 300)
        collectgarbage("setstepmul", 5000)

        self.coroutine_create = coroutine.create
        self.coroutines = {}
        coroutine.create = function(...)
            local co = self.coroutine_create(...)
            table.insert(self.coroutines, co)
            debug.sethook(co,profiler_hook_wrapper_by_call, "cr")
            return co
        end

        debug.sethook(profiler_hook_wrapper_by_call, "cr")
    else
        error("Profiler method must be 'time' or 'call'")
    end
end

--[[
    停止性能分析
]]
function Profiler:stop( ... )
    if Profiler.running ~= self then
        -- 如果没有启动则没有任何效果
        return
    end
    self.end_time = os.clock()

    if self.coroutine_create then
        coroutine.create = self.coroutine_create
        self.coroutine_create = nil
    end
    
    -- 停止性能分析
    debug.sethook(nil)
    if self.variant == "call" then
        -- 还原之前的垃圾回收设置
        collectgarbage("setpause", self.setpause) 
        collectgarbage("setstepmul", self.setstepmul)
    end
    collectgarbage("collect")
    collectgarbage("collect")
    Profiler.running = nil
end

--[[
    钩子函数入口
]]
function profiler_hook_wrapper_by_call( action )
    if Profiler.running == nil then
        debug.sethook(nil)
    end
    Profiler.running:analysis_call_info(action)
end

--[[
    分析函数调用信息
    @string action 函数调用类型 action return tail return
]]
function Profiler:analysis_call_info( action )
    -- 获取当前的调用信息,注意该函数有一定的损耗
    -- 0表示当前函数,即getinfo,1表示上一层调用即analysis_call_info,2表示再上一层,即profiler_hook_wrapper_by_call, 3即客户函数
    local caller_info = debug.getinfo(3, "Slfn")

    if caller_info == nil then
        return
    end

    local last_caller = self.callstack[1]

    if action == "call" then -- 进入函数,标记堆栈
        local this_caller = self:get_func_info_by_cache(caller_info)
        this_caller.parent = last_caller
        this_caller.clock_start = os.clock()
        this_caller.count = this_caller.count + 1
        table.insert(self.callstack, 1, this_caller)
    else
        table.remove(self.callstack, 1) -- 移除顶部堆栈,有可能粗发连续触发return

        if action == "tail return" then
            return
        end

        local this_caller = self.caller_cache[caller_info.func]
        if this_caller == nil then
            return
        end

        -- 计算本次函数调用时长
        this_caller.this_time = os.clock() - this_caller.clock_start 
        -- 该函数累加调用时间
        this_caller.time = this_caller.time + this_caller.this_time  

        -- 更新父类信息
        if this_caller.parent then
            local func = this_caller.func
            -- 更新父类中存储的该子函数的调用次数
            this_caller.parent.children[func] = (this_caller.parent.children[func] or 0) + 1
            -- 更新父类中存储的该子函数的总调用时间
            this_caller.parent.children_time[func] = (this_caller.parent.children_time[func] or 0) + this_caller.this_time
            
            if caller_info.name == nil then
                -- 统计无名函数调用时间
                this_caller.parent.unknow_child_time = this_caller.parent.unknow_child_time + this_caller.this_time
            else
                -- 统计有名函数调用时间
                this_caller.parent.name_child_time = this_caller.parent.name_child_time + this_caller.this_time
            end
        end
    end
end

--[[
    获取缓存里的函数信息
    @info 函数调用信息debug.getinfo返回的数据
]]
function Profiler:get_func_info_by_cache( info )
    local func = info.func
    local ret = self.caller_cache[func]
    if ret == nil then
        ret = {}
        ret.func = func
        ret.count = 0 -- 调用次数
        ret.time = 0 -- 时间
        ret.unknow_child_time = 0 --没有名字的函数的调用时间
        ret.name_child_time = 0 -- 有名字的函数的调用时间
        ret.children = {}
        ret.children_time = {}
        ret.func_info = info
        self.caller_cache[func] = ret
    end
    return ret
end

--格式化成表格样式
function Profiler:format_header(ordering,lines,totalTime)
    local TABL_REPORTS = {};
    local maxFileLen = 0;
    local maxFuncLen = 0;
    for i,func in ipairs(ordering) do
        local record = self.caller_cache[func]
        local reportInfo                         = {
            count  = record.count,
            timer  = record.time,
            src     = record.func_info.short_src,
            name    = record.func_info.name or "unknow",
            linedefined = record.func_info.linedefined,
            what = record.func_info.what,
            source = record.func_info.source;
        }

        reportInfo.src = self:pretty_name(func,true);

        --计算最长的名字
        if string.len(reportInfo.src) > maxFileLen and reportInfo.count > 0  then
            maxFileLen = string.len(reportInfo.src) + 1;
        end

        if string.len(reportInfo.name) > maxFuncLen and reportInfo.count > 0 then
            maxFuncLen = string.len(reportInfo.name) + 1;
        end

        table.insert(TABL_REPORTS,reportInfo);

    end

    if maxFileLen>=99 then --必须如此处理,不然会报错越界
        maxFileLen = 99;
    end

    --     if maxFuncLen>100 then
    --     maxFuncLen = 100;
    -- end


    -- print(maxFileLen,"maxFileLen")
    formatOutput                     = "| %-"..maxFileLen.."s: %-"..maxFuncLen.."s: %-"..timeWidth.."s: %-"..relaWidth.."s: %-"..callWidth.."s|\n"
    -- dump(formatOutput)
    formatHeader                     = string.format(formatOutput, "FILE", "FUNCTION", "TIME", "%", "Call count")
    divider = charRepetition(#formatHeader-1, "-").."\n"


    table.insert(lines, "\n"..divider)
    table.insert(lines, formatHeader)
    table.insert(lines, divider)

    local totalCount = 0;
    for i,reportInfo in ipairs(TABL_REPORTS) do
        if reportInfo.count > 0 and reportInfo.timer <= totalTime then
            local count             = string.format(formatFunCount, reportInfo.count)
            local timer             = string.format(formatFunTime, reportInfo.timer)
            local relTime           = string.format(formatFunRelative, (reportInfo.timer / totalTime) * 100)
            if timer == EMPTY_TIME then
                timer             = emptyToThis
                relTime           = emptyToThis
            end
            local outputLine    = string.format(formatOutput, reportInfo.src,reportInfo.name, timer, relTime, count)
            table.insert(lines, outputLine)

            totalCount = totalCount + reportInfo.count;
        end
    end
    table.insert(lines, divider)
    table.insert(lines, "\n\n")

    table.insert(lines, 2,"Total call count spent in profiled functions: " ..
        totalCount.. "\n\n")
end

--[[--
    生成报表table
    @return     table     报表
    @return     number     性能分析总时间
    @usage
        local new_profiler = import("bos.core.profiler")
        local profiler = new_profiler("call")
        profiler:start();
        -- do something
        profiler:stop();
        profiler:report();
]]
function Profiler:report()
    local lines = {};
    table.insert(lines,[[Lua Profile output created by profiler.lua. author: iwiniwin ]])
    table.insert(lines, "\n\n" )
    local total_time = self.end_time - self.start_time

    table.insert(lines, 1,"Total time spent in profiled functions: " ..
        string.format("%5.3g",total_time) .. "s\n\n")

    -- This is pretty awful.
    local terms = {}
    if self.variant == "time" then

    elseif self.variant == "call" then
        terms.capitalized = "Call"
        terms.single = "call"
        terms.pastverb = "called"
        local ordering = {}

        for func,record in pairs(self.caller_cache) do
            table.insert(ordering, func)
        end

        table.sort( ordering,
            function(a,b) return self.caller_cache[a].time > self.caller_cache[b].time end
        )

        --生成头部表格信息
        self:format_header(ordering,lines,total_time);

        for i,v in ipairs(ordering) do
            local func = ordering[i]
            local record = self.caller_cache[func]
            if record.count and record.count > 0 then --- 标记数量大于0的
                local thisfuncname = " " .. self:pretty_name(func) .. " "
                if string.len( thisfuncname ) < 42 then
                    thisfuncname =
                        string.rep( "-", math.floor((42 - string.len(thisfuncname))/2 )) .. thisfuncname
                    thisfuncname =
                        thisfuncname .. string.rep( "-", 42 - string.len(thisfuncname) )
                end

                --单个函数的总时间减去子函数的时间,获得自身的时间
                local timeinself = record.time - (record.unknow_child_time + record.name_child_time)
                if timeinself < 0 then
                    timeinself = 0;
                end

                local children =  record.unknow_child_time+record.name_child_time
                if children > record.time then
                    children = record.time
                end

                timeinself = timeinself * scale;

                table.insert(lines, string.rep( "-", 19 ) .. thisfuncname ..
                    string.rep( "-", 19 ) .. "\n" )

                table.insert(lines, terms.capitalized.." count:         " ..
                    string.format( "%4d", record.count ) .. "\n" )
                table.insert(lines, "Time spend total:       " ..
                    string.format( "%4.4f", record.time * scale) .. "s\n" )
                table.insert(lines, "Time spent in children: " ..
                    string.format("%4.4f",(children) * scale) ..
                    "s\n" )

                table.insert(lines, "Time spent in self:     " ..
                    string.format("%4.4f", timeinself) .. "s\n" )

                -- Report on each child in the form
                -- Child  <funcname> called n times and took a.bs
                local added_blank = 0
                for k,v in pairs(record.children) do
                    if added_blank == 0 then
                        table.insert(lines, "\n" ) -- extra separation line
                        added_blank = 1
                    end
                    table.insert(lines, "Child " .. self:pretty_name(k) ..
                        string.rep( " ", 41-string.len(self:pretty_name(k)) ) .. " " ..
                        terms.pastverb.." " .. string.format("%6d", v) )
                    table.insert(lines, " times. Took " ..
                        string.format("%4.5f", record.children_time[k] * scale ) .. "s\n" )

                end

                table.insert(lines, "\n" ) -- extra separation line

            end

        end
    end

    table.insert(lines, [[
END
]] )


    return lines,total_time
end

--[[--
    输出报表到文件
    @tparam     table     self    Profiler对象
    @tparam     string     outfile    文件名称
    @return   number 本次总共花费时间
    @usage
        local new_profiler = import("bos.core.profiler")
        local profiler = new_profiler("call")
        profiler:start();
        -- do something
        profiler:stop();
        profiler:dump_report_to_file("path");
]]
function Profiler.dump_report_to_file(self,outfile)
    local outfile = io.open(outfile, "w+" )
    local lines, total_time= self:report()
    for i,v in ipairs(lines) do
        outfile:write(v)
    end
    outfile:flush()
    outfile:close()
    return total_time
end


--[[
    美化名称,输出可以看懂的信息
    @tparam     function    func  函数
    @boolean    force 是否强制
]]
function Profiler:pretty_name(func,force)

    -- Only the data collected during the actual
    -- run seems to be correct.... why?
    local info = self.caller_cache[func].func_info

    local name = ""
    if info.what == "Lua" and force then
        name = "L:" .. info.short_src ..":" .. info.linedefined
        return name;
    end
    if info.what == "Lua" then
        name = "L:"
    end
    if info.what == "C" then
        name = "C:"
    end
    if info.what == "main" then
        name = " :"
    end
    if info.name == nil then
        name = name .. "<"..tostring(func) .. ">"
    else
        name = name .. info.name
    end

    if info.source then
        name = name .. "@" .. info.source
    else
        if info.what == "C" then
            name = name .. "@?"
        else
            name = name .. "@<string>"
        end
    end
    name = name .. ":"
    -- if info.what == "C" then
    --     name = name .. "unknow line"
    -- else
    --     name = name .. info.linedefined
    -- end
    name = name .. info.linedefined

    return name
end

return new_profiler



Download .txt
gitextract_vpofiopn/

├── .gitignore
├── README.md
├── _load.lua
├── build/
│   └── int64.c
├── core/
│   ├── component/
│   │   ├── component_base.lua
│   │   ├── component_extend.lua
│   │   └── component_factory.lua
│   ├── event/
│   │   ├── event.lua
│   │   └── event_system.lua
│   ├── object.lua
│   └── sandbox.lua
├── init.lua
├── lib/
│   ├── file.lua
│   ├── function.lua
│   ├── string.lua
│   ├── table.lua
│   └── time.lua
├── mvc/
│   ├── loader.lua
│   ├── module1/
│   │   ├── test1_ctr.lua
│   │   └── test1_view.lua
│   ├── module2/
│   │   ├── test2_ctr.lua
│   │   └── test2_view.lua
│   └── module_config.lua
├── pattern/
│   ├── AbstractFactoryPattern.lua
│   ├── AdapterPattern.lua
│   ├── CORPattern.lua
│   ├── CompositePattern.lua
│   ├── FactoryMethodPattern.lua
│   ├── ObserverPattern.lua
│   ├── ProxyPattern.lua
│   ├── StrategyPattern.lua
│   └── TemplatePattern.lua
├── test.lua
└── utils/
    ├── dump.lua
    ├── dump_to_file.lua
    ├── memory_monitor.lua
    └── profiler.lua
Download .txt
SYMBOL INDEX (18 symbols across 1 files)

FILE: build/int64.c
  function _int64 (line 9) | static int64_t
  function _pushint64 (line 48) | static inline void
  function int64_add (line 54) | static int
  function int64_sub (line 63) | static int
  function int64_mul (line 72) | static int
  function int64_div (line 81) | static int
  function int64_mod (line 93) | static int
  function _pow64 (line 105) | static int64_t
  function int64_pow (line 118) | static int
  function int64_unm (line 135) | static int
  function int64_new (line 143) | static int
  function int64_eq (line 169) | static int
  function int64_lt (line 179) | static int
  function int64_le (line 187) | static int
  function int64_len (line 195) | static int
  function tostring (line 202) | static int
  function make_mt (line 291) | static void
  function luaopen_int64 (line 312) | int
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (169K chars).
[
  {
    "path": ".gitignore",
    "chars": 7,
    "preview": "other/\n"
  },
  {
    "path": "README.md",
    "chars": 6516,
    "preview": "# LuaKit\nLua核心工具包,提供对面向对象,组件系统,mvc模块化加载,事件分发系统等常用模式的封装。同时提供打印,内存泄漏检测,字符串操作等常用工具类。\nPS:添加了注释的Lua源码可以参考<a href=\"https://git"
  },
  {
    "path": "_load.lua",
    "chars": 292,
    "preview": "--[[--\nLuaKit入口\n@module _load\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified tim"
  },
  {
    "path": "build/int64.c",
    "chars": 5989,
    "preview": "#include <lua.h>\n#include <lauxlib.h>\n#include <stdint.h>\n#include <math.h>\n#include <stdlib.h>\n#include <stdbool.h>\n\n//"
  },
  {
    "path": "core/component/component_base.lua",
    "chars": 1198,
    "preview": "--[[--组件基类  \r\n注意:所有组件必须继承该类\r\n@module ComponentBase\r\n@author iwiniwin\r\n\r\nDate   2020-01-16 13:27:14\r\nLast Modified by   i"
  },
  {
    "path": "core/component/component_extend.lua",
    "chars": 6406,
    "preview": "--[[--组件扩展\n赋予类绑定解绑组件的能力\n@module ComponentExtend\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin"
  },
  {
    "path": "core/component/component_factory.lua",
    "chars": 699,
    "preview": "--[[--组件工厂类  \n@module ComponentFactory\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Mod"
  },
  {
    "path": "core/event/event.lua",
    "chars": 359,
    "preview": "--[[--事件配置,便于统一管理项目中所有使用的事件\n@module Event\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast "
  },
  {
    "path": "core/event/event_system.lua",
    "chars": 4927,
    "preview": "--[[--事件分发系统\r\n@module EventSystem\r\n@author iwiniwin\r\n\r\nDate   2020-03-30 14:23:50\r\nLast Modified by   iwiniwin\r\nLast Mod"
  },
  {
    "path": "core/object.lua",
    "chars": 9355,
    "preview": "--[[--用于模拟面向对象\n@usage require(\"core/object\")\n@module object\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified b"
  },
  {
    "path": "core/sandbox.lua",
    "chars": 1027,
    "preview": "--[[--lua沙盒\n@module sandbox\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time "
  },
  {
    "path": "init.lua",
    "chars": 582,
    "preview": "--[[--\nLuaKit初始化函数,初始化全局变量\n@module init\n@author iwiniwin\n\nDate   2021-09-04 20:20:39\nLast Modified by   iwiniwin\nLast Mo"
  },
  {
    "path": "lib/file.lua",
    "chars": 1083,
    "preview": "--[[--文件操作\n@module File\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020"
  },
  {
    "path": "lib/function.lua",
    "chars": 314,
    "preview": "--[[--常用函数集合\n@module Function\n@author iwiniwin\n\nDate   2020-02-27 13:27:14\nLast Modified by   iwiniwin\nLast Modified tim"
  },
  {
    "path": "lib/string.lua",
    "chars": 29615,
    "preview": "--[[--字符串操作\n@module string\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2"
  },
  {
    "path": "lib/table.lua",
    "chars": 15485,
    "preview": "--[[--table操作\n@module table\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time "
  },
  {
    "path": "lib/time.lua",
    "chars": 659,
    "preview": "--[[--时间操作\n@module time\n@author iwiniwin\n\nDate   2020-01-16 13:27:14\nLast Modified by   iwiniwin\nLast Modified time 2020"
  },
  {
    "path": "mvc/loader.lua",
    "chars": 1071,
    "preview": "--[[--\n模块加载器\n@module Loader\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified time "
  },
  {
    "path": "mvc/module1/test1_ctr.lua",
    "chars": 689,
    "preview": "--[[--Test1Ctr 示例Ctr\n@module Test1Ctr\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modi"
  },
  {
    "path": "mvc/module1/test1_view.lua",
    "chars": 1048,
    "preview": "--[[--ldoc desc\nTest1View 示例View\n@module Test1View\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwini"
  },
  {
    "path": "mvc/module2/test2_ctr.lua",
    "chars": 697,
    "preview": "--[[--ldoc desc\nTest2Ctr 示例Ctr\n@module Test2Ctr\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin"
  },
  {
    "path": "mvc/module2/test2_view.lua",
    "chars": 1048,
    "preview": "--[[--ldoc desc\nTest2View 示例View\n@module Test2View\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwini"
  },
  {
    "path": "mvc/module_config.lua",
    "chars": 389,
    "preview": "--[[--\r\nModuleConfig\r\n@module ModuleConfig\r\n@author iwiniwin\r\n\r\nDate   2019-11-15 19:20:39\r\nLast Modified by   iwiniwin\r"
  },
  {
    "path": "pattern/AbstractFactoryPattern.lua",
    "chars": 2703,
    "preview": "--[[--\n抽象工厂模式\n@module AbstractFactoryPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLa"
  },
  {
    "path": "pattern/AdapterPattern.lua",
    "chars": 1603,
    "preview": "--[[--\n适配器模式\n@module AdapterPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modifi"
  },
  {
    "path": "pattern/CORPattern.lua",
    "chars": 3359,
    "preview": "--[[--\n责任链模式\n@module CORPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified t"
  },
  {
    "path": "pattern/CompositePattern.lua",
    "chars": 2536,
    "preview": "--[[--\n组合模式\n@module CompositePattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modif"
  },
  {
    "path": "pattern/FactoryMethodPattern.lua",
    "chars": 1775,
    "preview": "--[[--\n工厂方法模式\n@module FactoryMethodPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast"
  },
  {
    "path": "pattern/ObserverPattern.lua",
    "chars": 2682,
    "preview": "--[[--\n观察者模式\n@module ObserverPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modif"
  },
  {
    "path": "pattern/ProxyPattern.lua",
    "chars": 151,
    "preview": "--[[--\r\n@module ProxyPattern\r\n@author iwiniwin\r\n\r\nDate   2019-11-15 19:20:39\r\nLast Modified by   iwiniwin\r\nLast Modified"
  },
  {
    "path": "pattern/StrategyPattern.lua",
    "chars": 2911,
    "preview": "--[[--策略模式\n@module StrategyPattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modifie"
  },
  {
    "path": "pattern/TemplatePattern.lua",
    "chars": 2350,
    "preview": "--[[--模板方法模式\n@module TemplatePattern\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modif"
  },
  {
    "path": "test.lua",
    "chars": 4013,
    "preview": "--[[--\nLuaKit测试用例\n@module test\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified ti"
  },
  {
    "path": "utils/dump.lua",
    "chars": 3053,
    "preview": "--[[--\n格式化输出table(格式化过程中,排序操作会比较耗时)\n@module dump\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwi"
  },
  {
    "path": "utils/dump_to_file.lua",
    "chars": 4038,
    "preview": "--[[--\n序列化lua table\n@module dump_to_file\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast M"
  },
  {
    "path": "utils/memory_monitor.lua",
    "chars": 3609,
    "preview": "--[[--\nlua内存泄漏检测工具\n@module MemoryMonitor\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast M"
  },
  {
    "path": "utils/profiler.lua",
    "chars": 15210,
    "preview": "--[[--\nlua性能分析工具\n@module profiler\n@author iwiniwin\n\nDate   2019-11-15 19:20:39\nLast Modified by   iwiniwin\nLast Modified"
  }
]

About this extraction

This page contains the full source code of the iwiniwin/LuaKit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (136.2 KB), approximately 43.5k tokens, and a symbol index with 18 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!