File size: 3,323 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
--- An object which can resolve a key to a value.
-- Context objects can be nested to create a complex value resolution structure.
-- The global function ResolveValue allows resolving a tuple to a value in an arbitrary context.

DefineClass.Context = {
	__parents = {},
	__hierarchy_cache = true,	
}

function Context:new(obj)
	return setmetatable(obj or {}, self)
end

function Context:ResolveValue(key)
	local value = rawget(self, key)
	if value ~= nil then return value end
	for _, sub_context in ipairs(self) do
		value = ResolveValue(sub_context, key)
		if value ~= nil then return value end
	end
end

-- change __index method to allow full member resolution without warning
function OnMsg.ClassesBuilt()
	local context_class = g_Classes.Context
	context_class.__index = function (self, key)
		if type(key) == "string" then
			return rawget(context_class, key) or context_class.ResolveValue(self, key)
		end
	end
end

function Context:IsKindOf(class)
	if IsKindOf(self, class) then return true end
	for _, sub_context in ipairs(self) do
		if IsKindOf(sub_context, "Context") and sub_context:IsKindOf(class) or IsKindOf(sub_context, class) then
			return true
		end
	end
end

function Context:IsKindOfClasses(...)
	if IsKindOfClasses(self, ...) then return true end
	for _, sub_context in ipairs(self) do
		if IsKindOf(sub_context, "Context") and sub_context:IsKindOfClasses(...) or IsKindOfClasses(sub_context, ...) then
			return true
		end
	end
end

function ForEachObjInContext(context, f, ...)
	if not context then return end
	if IsKindOf(context, "Context") then
		for _, sub_context in ipairs(context) do
			ForEachObjInContext(sub_context, f, ...)
		end
	else
		f(context, ...)
	end
end

function SubContext(context, t)
	assert(not IsKindOf(t, "PropertyObject"))
	t = t or {}
	if IsKindOf(context, "PropertyObject") or type(context) ~= "table" then
		t[#t + 1] = context
	elseif type(context) == "table" then
		for _, obj in ipairs(context) do
			t[#t + 1] = obj
		end
		for k, v in pairs(context) do
			if rawget(t, k) == nil then
				t[k] = v
			end
		end
	end
	return Context:new(t)
end

function ResolveValue(context, key, ...)
	if key == nil then return context end
	if type(context) == "table" then
		if IsKindOfClasses(context, "Context", "PropertyObject") then
			return ResolveValue(context:ResolveValue(key), ...)
		end
		return ResolveValue(rawget(context, key), ...)
	end
end

function ResolveFunc(context, key)
	if key == nil then return end
	if type(context) == "table" then
		if IsKindOf(context, "Context") then
			local f = rawget(context, key)
			if type(f) == "function" then
				return f
			end
			for _, sub_context in ipairs(context) do
				local f, obj = ResolveFunc(sub_context, key)
				if f ~= nil then return f, obj end
			end
			return
		end
		if IsKindOf(context, "PropertyObject") and context:HasMember(key) then
			local f = context[key]
			if type(f) == "function" then return f, context end
		else
			local f = rawget(context, key)
			if f == false or type(f) == "function" then return f end
		end
	end
end

function ResolvePropObj(context)
	if IsKindOf(context, "PropertyObject") then
		return context
	end
	if IsKindOf(context, "Context") then
		for _, sub_context in ipairs(context) do
			local obj = ResolvePropObj(sub_context)
			if obj then return obj end
		end
	end
end