1+ /*
2+ * Copyright (C) 2019 Felipe Escoto <[email protected] > 3+ *
4+ * This program or library is free software; you can redistribute it
5+ * and/or modify it under the terms of the GNU Lesser General Public
6+ * License as published by the Free Software Foundation; either
7+ * version 3 of the License, or (at your option) any later version.
8+ *
9+ * This library is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+ * Lesser General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU Lesser General
15+ * Public License along with this library; if not, write to the
16+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17+ * Boston, MA 02110-1301 USA.
18+ */
19+
20+ public abstract class Spice.FileFormat.JsonObject : GLib .Object {
21+
22+ [Signal (no_recurse = true , run = "first ", action = true , no_hooks = true , detailed = true )]
23+ public signal void changed (string changed_property );
24+
25+ public Json . Object object { get ; construct; }
26+ public JsonObject ? parent_object { get ; construct; default = null ; }
27+
28+ private ObjectClass obj_class;
29+
30+ public JsonObject.from_object (Json .Object object ) {
31+ Object (object : object );
32+ }
33+
34+ construct {
35+ obj_class = (ObjectClass ) get_type (). class_ref ();
36+
37+ var properties = obj_class. list_properties ();
38+ foreach (var prop in properties) {
39+ load_key (prop. name, object );
40+ }
41+ }
42+
43+ public void connect_signals () {
44+ notify. connect (handle_notify);
45+ }
46+
47+ private void handle_notify (Object sender , ParamSpec property ) {
48+ notify. disconnect (handle_notify);
49+
50+ save_on_object (property. name);
51+ call_verify (property. name);
52+
53+ notify. connect (handle_notify);
54+ }
55+
56+ private void call_verify (string key ) {
57+ if (key == " object" || key == " parent-object" ) {
58+ return ;
59+ }
60+
61+ if (internal_changed (key)) {
62+ changed (key);
63+ }
64+ }
65+
66+ /**
67+ * For reacting to internal changes.
68+ *
69+ * Return false to prevent the triggering of the changed signal
70+ */
71+ protected virtual bool internal_changed (string key ) {
72+ return true ;
73+ }
74+
75+ /**
76+ * Used when a JSON Property has a different name than a GObject property.
77+ * This should return the name of the JSON property that you want to get from a gobject string.
78+ *
79+ * For example. GObject properties internally use "-" instead of "_"
80+ */
81+ protected virtual string key_override (string key ) {
82+ return key;
83+ }
84+
85+ private void load_key (string key , Json .Object source_object ) {
86+ if (key == " object" || key == " parent-object" ) {
87+ return ;
88+ }
89+
90+ string get_key = key_override (key);
91+
92+ if (! source_object. has_member (get_key)) {
93+ return ;
94+ }
95+
96+ var prop = obj_class. find_property (key);
97+
98+ var type = prop. value_type;
99+ var val = Value (type);
100+
101+ if (val. type () == prop. value_type) {
102+ if (type == typeof (int ))
103+ set_property (prop. name, (int ) source_object. get_int_member (get_key));
104+ else if (type == typeof (uint ))
105+ set_property (prop. name, (uint ) source_object. get_int_member (get_key));
106+ else if (type == typeof (double ))
107+ set_property (prop. name, source_object. get_double_member (get_key));
108+ else if (type == typeof (string ))
109+ set_property (prop. name, source_object. get_string_member (get_key));
110+ else if (type == typeof (bool ))
111+ set_property (prop. name, source_object. get_boolean_member (get_key));
112+ else if (type == typeof (int64 ))
113+ set_property (prop. name, source_object. get_int_member (get_key));
114+ else if (type. is_a (typeof (JsonObject ))) {
115+ var object = source_object. get_object_member (get_key);
116+ if (val. get_object () == null ) {
117+ set_property (prop. name, Object . new (type, "object", source_object, "parent-object", this));
118+ } else {
119+ var json_object = (JsonObject ) val. get_object ();
120+ json_object. override_properties_from_json (object );
121+ }
122+ } else if (type. is_a (typeof (Spice . FileFormat . JsonObjectArray ))) {
123+ if (val. get_object () == null ) {
124+ set_property (prop. name, Object . new (type, "object", source_object, "property_name", prop.name));
125+ } else {
126+ // Set elements to existing array
127+ }
128+ } else if (type == typeof (string [])) {
129+ var list = new Gee .LinkedList<string> ();
130+ source_object. get_array_member (get_key). get_elements (). foreach ((node) = > {
131+ list. add (node. get_string ());
132+ });
133+ set_property (prop. name, list. to_array ());
134+ } else {
135+ print (" type error%s\n " , type. name ());
136+ }
137+ } else {
138+ print (" Unsupported settings type '%s ' in object\n " , type. name ());
139+ }
140+ }
141+
142+ protected string get_string_property (string key ) {
143+ var prop = obj_class. find_property (key);
144+
145+ var type = prop. value_type;
146+ var val = Value (type);
147+ this . get_property (prop. name. down (), ref val);
148+
149+ if (val. type () == prop. value_type) {
150+ if (type == typeof (int ))
151+ return ((int ) val). to_string ();
152+ else if (type == typeof (uint ))
153+ return ((uint ) val). to_string ();
154+ else if (type == typeof (double ))
155+ return ((double ) val). to_string ();
156+ else if (type == typeof (string ))
157+ return ((string ) val). to_string ();
158+ else if (type == typeof (bool ))
159+ return ((bool ) val). to_string ();
160+ else if (type == typeof (int64 ))
161+ return ((int64 ) val). to_string ();
162+ }
163+
164+ assert_not_reached ();
165+ }
166+
167+ /*
168+ * Runs when you set a vala property on the object to store the value in the internal JSON class
169+ */
170+ private void save_on_object (string key ) {
171+ if (key == " object" || key == " parent-object" ) {
172+ return ;
173+ }
174+
175+ string get_key = key_override (key);
176+
177+ var prop = obj_class. find_property (key);
178+
179+ // Do not attempt to save a non-mapped key
180+ if (prop == null )
181+ return ;
182+
183+ var type = prop. value_type;
184+ var val = Value (type);
185+ this . get_property (prop. name, ref val);
186+
187+ if (val. type () == prop. value_type) {
188+ if (type == typeof (int )) {
189+ if (val. get_int () != object . get_int_member (key)) {
190+ object . set_int_member (get_key, val. get_int ());
191+ }
192+ } else if (type == typeof (uint )) {
193+ if (val. get_uint () != object . get_int_member (key)) {
194+ object . set_int_member (get_key, val. get_uint ());
195+ }
196+ } else if (type == typeof (int64 )) {
197+ if (val. get_int64 () != object . get_int_member (key)) {
198+ object . set_int_member (get_key, val. get_int64 ());
199+ }
200+ } else if (type == typeof (double )) {
201+ if (val. get_double () != object . get_double_member (key)) {
202+ object . set_double_member (key, val. get_double ());
203+ }
204+ } else if (type == typeof (string )) {
205+ if (val. get_string () != object . get_string_member (key)) {
206+ object . set_string_member (key, val. get_string ());
207+ }
208+ } else if (type == typeof (string [])) {
209+ // string[] strings = null;
210+ // this.get (key, &strings);
211+ // if (strings != schema.get_strv (key)) {
212+ // schema.set_strv (key, strings);
213+ // }
214+ } else if (type == typeof (bool )) {
215+ if (val. get_boolean () != object . get_boolean_member (key)) {
216+ object . set_boolean_member (key, val. get_boolean ());
217+ }
218+ }
219+ }
220+ }
221+
222+ /**
223+ * Get's a string representation of this object. Useful for serialization
224+ */
225+ public string to_string (bool prettyfied ) {
226+ var node = new Json .Node .alloc ();
227+ node. set_object (object );
228+
229+ return Json . to_string (node, prettyfied);
230+ }
231+
232+ /**
233+ * Got a new Json Object and want to update it's properties. Do it from here!
234+ */
235+ public void override_properties_from_json (Json .Object new_object ) {
236+ notify. disconnect (handle_notify);
237+
238+ var properties = obj_class. list_properties ();
239+ foreach (var prop in properties) {
240+ var prop_name = prop. name;
241+ if (prop_name == " object" || prop_name == " parent-object" ) {
242+ continue ;
243+ }
244+
245+ string get_key = key_override (prop_name);
246+ if (! new_object. has_member (get_key)) {
247+ return ;
248+ }
249+
250+ var type = prop. value_type;
251+ var original_value = Value (type);
252+
253+ if (ParamFlags . READABLE in prop. flags) {
254+ this . get_property (prop_name. down (), ref original_value);
255+ }
256+
257+ bool change_prop = false ;
258+
259+ if (type == typeof (int )) {
260+ change_prop = (original_value. get_int () != new_object. get_int_member (get_key));
261+ } else if (type == typeof (uint )) {
262+ change_prop = (original_value. get_uint () != new_object. get_int_member (get_key));
263+ } else if (type == typeof (int64 )) {
264+ change_prop = (original_value. get_int64 () != new_object. get_int_member (get_key));
265+ } else if (type == typeof (double )) {
266+ change_prop = (original_value. get_double () != new_object. get_double_member (get_key));
267+ } else if (type == typeof (string )) {
268+ change_prop = (original_value. get_string () != new_object. get_string_member (get_key));
269+ } else if (type == typeof (string [])) {
270+ // string[] strings = null;
271+ // this.get (key, &strings);
272+ // if (strings != schema.get_strv (key)) {
273+ // schema.set_strv (key, strings);
274+ // }
275+ } else if (type == typeof (bool )) {
276+ change_prop = (original_value. get_boolean () != new_object. get_boolean_member (get_key));
277+ } else if (type. is_a (typeof (JsonObject ))) {
278+ var object = new_object. get_object_member (get_key);
279+
280+ var json_object = (JsonObject ) original_value. get_object ();
281+ json_object. override_properties_from_json (object );
282+ }
283+
284+ if (change_prop) {
285+ load_key (prop. name, new_object);
286+ changed (prop. name);
287+ }
288+ }
289+
290+ notify. connect (handle_notify);
291+ }
292+ }
293+
294+ public abstract class Spice.FileFormat.JsonObjectArray : Object {
295+ public unowned Json . Object object { get ; construct; }
296+ public string property_name { get ; construct; }
297+
298+ public JsonObjectArray (Json .Object object , string property_name ) {
299+ Object (object : object , property_name: property_name);
300+ }
301+
302+ construct {
303+ load_array ();
304+ }
305+
306+ /**
307+ * Can be overriten to add more than one type of item into the array
308+ */
309+ protected virtual void load_array () {
310+ object . get_array_member (property_name). get_elements (). foreach ((node) = > {
311+ add_to_list ((FileFormat . JsonObject ) Object . new (get_type_of_array (),
312+ " object" , node. get_object (),
313+ " parent-object" , null ));
314+ });
315+ }
316+
317+ public abstract void add_to_list (FileFormat .JsonObject json_object );
318+ public abstract Type get_type_of_array ();
319+ }
0 commit comments