1 /**
2 Copyright: Copyright (c) 2013-2014 Andrey Penechko.
3 License: a$(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module anchovy.gui.skin.jsonguiskinparser;
8 
9 import std.array;
10 import std.conv;
11 import std.json;
12 import std.stdio;
13 
14 import anchovy.core.types;
15 
16 import anchovy.gui;
17 
18 
19 //version = debug_parser;
20 
21 class SkinParserException : Exception
22 {
23 	this(string msg, string file = __FILE__, size_t line = __LINE__)
24 	{
25 		super(msg, file, line);
26 	}
27 }
28 
29 alias JSONValue[string] jsonObject;
30 alias JSONValue[] jsonArray;
31 
32 static string[JSON_TYPE] jsonTypeNames;
33 static this()
34 {
35 	jsonTypeNames =
36 		[JSON_TYPE.STRING : "string",
37 		 JSON_TYPE.INTEGER : "int",
38 		 JSON_TYPE.UINTEGER : "uint",
39 		 JSON_TYPE.FLOAT : "float",
40 		 JSON_TYPE.OBJECT : "object",
41 		 JSON_TYPE.ARRAY : "array",
42 		 JSON_TYPE.TRUE : "true",
43 		 JSON_TYPE.FALSE : "false",
44 		 JSON_TYPE.NULL : "null",];
45 }
46 
47 static const string returnDefault = "if (value.type == JSON_TYPE.NULL) return defaultValue;";
48 
49 class JsonGuiSkinParser
50 {
51 	string[] warnings;
52 	string[] errors;
53 
54 	void warn(string message)
55 	{
56 		warnings ~= message;
57 	}
58 
59 	GuiSkin parse(string skinData)
60 	{
61 		GuiSkin skin = new GuiSkin();
62 
63 		try
64 		{
65 			auto jsonValue = parseJSON(skinData);
66 
67 			skin.name = getValue!string("name", jsonValue);
68 			skin.textureFilename = getValue!string("image", jsonValue);
69 			skin.fontInfos = parseFonts(getValue!jsonArray("fonts", jsonValue));
70 
71 			foreach(i, ref value; getValue!jsonArray("styles", jsonValue))
72 			{
73 				string styleName = getValue!string("name", value);
74 
75 				if (styleName == "")
76 				{
77 					warn("No style name was specified for style no " ~ to!string(i + 1) ~ " in skin " ~ skin.name);
78 					continue;
79 				}
80 
81 				skin.styles[styleName] = parseStyle(value, styleName);
82 			}
83 
84 		}
85 		catch(JSONException exception)
86 		{
87 			throw new SkinParserException(exception.msg);
88 		}
89 
90 		if (!warnings.empty)
91 		{
92 			foreach(warning; warnings)
93 				writeln("Warning: ", warning);
94 
95 			warnings = [];
96 		}
97 
98 		return skin;
99 	}
100 
101 	/**
102 	 * len 	Result
103 	 * 1 	Поля будут установлены одновременно от каждого края элемента.
104 	 * 2 	Первое значение устанавливает поля от верхнего и нижнего края, второе — от левого и правого.
105 	 * 3 	Первое значение задает поле от верхнего края, второе — одновременно от левого и правого края, а третье — от нижнего края.
106 	 * 4 	Поочередно устанавливается поля от верхнего, правого, нижнего и левого края.
107 	 */
108 
109 	GuiStyle parseStyle(ref JSONValue value, string styleName)
110 	{
111 		expect(JSON_TYPE.OBJECT, value);
112 
113 		GuiStyle parsedStyle = new GuiStyle();
114 		GuiStyleState nullState;
115 
116 		parsedStyle.states["normal"] = parseStyleState(value, nullState);
117 		parsedStyle.fontName = getValue!string("font", value, "normal");
118 
119 		jsonArray states = getValue!jsonArray("states", value);
120 
121 		foreach(i, ref state; states)
122 		{
123 			string stateName = getValue!string("state", state);
124 
125 			if (stateName == "")
126 			{
127 				warn("No state name was specified for state no "~to!string(i+1)~" in style "~styleName);
128 				continue;
129 			}
130 
131 			version(debug_parser) writeln("stateName: ", stateName);
132 
133 			parsedStyle.states[stateName] = parseStyleState(state, parsedStyle.states["normal"]);
134 		}
135 
136 		return parsedStyle;
137 	}
138 
139 	GuiStyleState parseStyleState(ref JSONValue stateValue, ref GuiStyleState globalState)
140 	{
141 		GuiStyleState parsedStyleState;
142 		expect(JSON_TYPE.OBJECT, stateValue);
143 
144 		jsonArray fixedBordersValue = getValue!jsonArray("fixedBorders", stateValue);
145 		parsedStyleState.fixedBorders = RectOffset(parseRectOffset(fixedBordersValue, globalState.fixedBorders.arrayof));
146 
147 		jsonArray contentPaddingValue = getValue!jsonArray("contentPadding", stateValue);
148 		parsedStyleState.contentPadding = RectOffset(parseRectOffset(contentPaddingValue, globalState.contentPadding.arrayof));
149 
150 		jsonArray rectValue = getValue!jsonArray("rect", stateValue);
151 		parsedStyleState.atlasRect = Rect(parseRect(rectValue, globalState.atlasRect.arrayof));
152 
153 		jsonArray outlineValue = getValue!jsonArray("outline", stateValue);
154 		parsedStyleState.outline = RectOffset(parseRectOffset(outlineValue, globalState.outline.arrayof));
155 
156 		jsonArray minSize = getValue!jsonArray("minSize", stateValue);
157 		parsedStyleState.minSize = parseSize(minSize, ivec2(parsedStyleState.atlasRect.width, parsedStyleState.atlasRect.height));
158 
159 		jsonArray maxSize = getValue!jsonArray("maxSize", stateValue);
160 		parsedStyleState.maxSize = parseSize(maxSize, ivec2(0, 0));
161 
162 		return parsedStyleState;
163 	}
164 
165 	FontInfo[] parseFonts(jsonArray inArray)
166 	{
167 		FontInfo[] outFonts;
168 
169 		foreach(font; inArray)
170 		{
171 			outFonts ~= FontInfo(getValue!string("name", font),
172 			                     getValue!string("file", font),
173 			                     getValue!uint("size", font),
174 			                     getValue!int("verticalOffset", font));
175 		}
176 
177 		version(debug_parser) writeln(outFonts);
178 
179 		return outFonts;
180 	}
181 
182 private:
183 
184 	int[4] parseRect(jsonArray inArray, int[4] defaultValue = [0, 0, 0, 0])
185 	{
186 		if (inArray.length == 0) return defaultValue;
187 
188 		int[4] outArray;
189 
190 		foreach(i, ref element; inArray)
191 		{
192 			if (i > 3)
193 			{
194 				warn("Rect with more than 4 values found");
195 				break;
196 			}
197 
198 			outArray[i] = getValue!int(element);
199 		}
200 
201 		return outArray;
202 	}
203 
204 	ivec2 parsePoint(jsonArray inArray, ivec2 defaultValue = ivec2(0, 0))
205 	{
206 		if (inArray.length == 0)
207 			return defaultValue;
208 
209 		if (inArray.length == 1)
210 			return ivec2(getValue!int(inArray[0]), 0);
211 
212 		return ivec2(getValue!int(inArray[0]), getValue!int(inArray[1]));
213 	}
214 
215 	ivec2 parseSize(jsonArray inArray, ivec2 defaultValue = ivec2(0, 0))
216 	{
217 		if (inArray.length == 0)
218 			return defaultValue;
219 
220 		if (inArray.length == 1)
221 		{
222 			uint size = getValue!uint(inArray[0]);
223 
224 			return ivec2(size, size);
225 		}
226 
227 		return ivec2(getValue!uint(inArray[0]), getValue!uint(inArray[1]));
228 	}
229 
230 	int[4] parseRectOffset(jsonArray inArray, int[4] defaultValue = [0, 0, 0, 0])
231 	{
232 		if (inArray.length == 0)
233 		{
234 			return defaultValue;
235 		}
236 		else if (inArray.length == 1)
237 		{
238 			int val = getValue!int(inArray[0]);
239 
240 			return [val, val, val, val];
241 		}
242 		else if (inArray.length == 2)
243 		{
244 			int vert = getValue!int(inArray[0]);
245 			int hor = getValue!int(inArray[1]);
246 
247 			return [hor, hor, vert, vert];
248 		}
249 		else if (inArray.length == 3)
250 		{
251 			int top = getValue!int(inArray[0]);
252 			int hor = getValue!int(inArray[1]);
253 			int bottom = getValue!int(inArray[2]);
254 
255 			return [hor, hor, top, bottom];
256 		}
257 		else
258 		{
259 			int top = getValue!int(inArray[0]);
260 			int right = getValue!int(inArray[1]);
261 			int bottom = getValue!int(inArray[2]);
262 			int left = getValue!int(inArray[3]);
263 
264 			if (inArray.length > 4) warn("Rect with more than 4 values found");
265 
266 			return [left, right, top, bottom];
267 		}
268 	}
269 
270 	void expect(JSON_TYPE type, ref JSONValue value, string file = __FILE__, size_t line = __LINE__)
271 	{
272 		if(value.type != type && value.type != JSON_TYPE.NULL)
273 		{
274 			throw new SkinParserException("Wrong JSON type. " ~
275 			                              jsonTypeNames[value.type] ~
276 			                              " found while " ~
277 			                              jsonTypeNames[type] ~
278 			                              " expected", file, line);
279 		}
280 	}
281 
282 	T getValue(T)(string key, ref JSONValue objectValue, T defaultValue = T.init)
283 	{
284      	JSONValue* targetValue = key in objectValue.object;
285 
286 	 	if (targetValue is null)
287 	 	{
288 	 		return defaultValue;
289 	 	}
290 	 	else
291 	 	{
292 	 		return getValue!T(*targetValue, defaultValue);
293 	 	}
294 	}
295 
296 	T getValue(T)(ref JSONValue value, T defaultValue = T.init)
297 	{
298 		static if (is(T : string))
299 		{
300 			expect(JSON_TYPE.STRING, value);
301 			mixin(returnDefault);
302 			return value.str;
303 		}
304 		else static if (is(T : int))
305 		{
306 			expect(JSON_TYPE.INTEGER, value);
307 			mixin(returnDefault);
308 			return cast(int)value.integer;
309 		}
310 		else static if (is(T : uint))
311 		{
312 			expect(JSON_TYPE.UINTEGER, value);
313 			mixin(returnDefault);
314 			return cast(uint)value.uinteger;
315 		}
316 		else static if(is(T : float))
317 		{
318 			expect(JSON_TYPE.FLOAT, value);
319 			mixin(returnDefault);
320 			return cast(float)value.floating;
321 		}
322 		else static if(is(T : jsonObject))
323 		{
324 			expect(JSON_TYPE.OBJECT, value);
325 			mixin(returnDefault);
326 			return value.object;
327 		}
328 		else static if(is(T : jsonArray))
329 		{
330 			expect(JSON_TYPE.ARRAY, value);
331 			mixin(returnDefault);
332 			return value.array;
333 		}
334 		else static if(is(T : bool))
335 		{
336 			if(value.type == JSON_TYPE.TRUE)
337 				return true;
338 			else if (value.type == JSON_TYPE.FALSE)
339 				return false;
340 			else if (value.type == JSON_TYPE.NULL)
341 				return defaultValue;
342 			else throw new SkinParserException("Wrong JSON type. " ~
343 			                                   jsonTypeNames[value.type] ~
344 			                                   " found while boolean expected");
345 		}
346 	}
347 }