src/Project/Gtk.vala
[app.Builder.js] / src / Project / Project.vala
1 //<Script type="text/javascript">
2
3 /**
4  * Project Object
5  * 
6  * Projects can only contain one directory... - it can import other projects..(later)
7  * 
8  * we need to sort out that - paths is currently a key/value array..
9  * 
10  * 
11  * 
12  */
13 namespace Project {
14          public errordomain Error {
15                 INVALID_TYPE,
16                 NEED_IMPLEMENTING,
17                 MISSING_FILE,
18                 INVALID_VALUE,
19                 INVALID_FORMAT
20         }
21
22         // static array of all projects.
23         public Gee.HashMap<string,Project>  projects;
24         
25         
26         public bool  projects_loaded = false;
27
28         
29         public class Project : Object {
30                 
31                 public signal void on_changed (); 
32         
33                 public string id;
34                 public string fn = ""; // just a md5...
35                 public string name = "";
36                 public string runhtml = "";
37                 public string base_template = "";
38                 public string rootURL = "";
39                 public Gee.HashMap<string,string> paths;
40                 public Gee.HashMap<string,JsRender.JsRender> files ;
41                 //tree : false,
42                 public  string xtype;
43                 
44                 public Json.Object json_project_data;
45                 public Palete.RooDatabase roo_database;
46                  
47                 bool is_scanned; 
48            
49                 
50                 public Project (string path) {
51                         
52                         this.name = GLib.Path.get_basename(path); // default..
53                         this.json_project_data = new Json.Object();
54                         
55                         this.is_scanned = false;
56                         this.paths = new Gee.HashMap<string,string>();
57                         this.files = new Gee.HashMap<string,JsRender.JsRender>();
58                         //XObject.extend(this, cfg);
59                         //this.files = { }; 
60                         if (path.length > 0) {
61                                 this.paths.set(path, "dir");
62                         }
63                         // dummy roo database...
64                         this.initRooDatabase();
65                         
66                         
67                         
68                 }
69                 public void  initRooDatabase()
70                 {
71                          
72                         this.roo_database = new Palete.RooDatabase.from_project(this);
73                 }
74                 
75                 
76                 
77                 public static void loadAll(bool force = false)
78                 {
79                         if (projects_loaded && !force) {
80                                 return;
81                         }
82
83                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
84                         var dir = File.new_for_path(dirname);
85                                 if (!dir.query_exists()) {
86                                 dir.make_directory();
87                                 return;
88                         }
89                         projects = new  Gee.HashMap<string,Project>();
90                           
91                    
92                         try {
93                                 var file_enum = dir.enumerate_children(
94                                                                 GLib.FileAttribute.STANDARD_DISPLAY_NAME, 
95                                         GLib.FileQueryInfoFlags.NONE, 
96                                         null
97                                 );
98                                 
99                                  
100                                 FileInfo next_file; 
101                                 while ((next_file = file_enum.next_file(null)) != null) {
102                                                 var fn = next_file.get_display_name();
103                                         if (!Regex.match_simple("\\.json$", fn)) {
104                                                 continue;
105                                         }
106                                         factoryFromFile(dirname + "/" + fn);
107                                 }       
108                         } catch(Error e) {
109                                 GLib.warning("oops - something went wrong scanning the projects\n");
110                         }
111                         
112
113                 }
114
115                 public static Gee.ArrayList<Project> allProjectsByName()
116                 {
117                         var ret = new Gee.ArrayList<Project>();
118                         var iter = projects.map_iterator();
119                                 while (iter.next()) {
120                                         ret.add(iter.get_value());
121                                 }
122                         // fixme -- sort...
123                         return ret;
124                 
125                 }
126                 
127                 public static Project getProject(string name)
128                 {
129                         
130                         var iter = projects.map_iterator();
131                         while (iter.next()) {
132                                 if (iter.get_value().name == name) {
133                                         return iter.get_value();
134                                 }
135                                 
136                         }
137                         
138                         return null;
139                 
140                 }
141                 public static Project getProjectByHash(string fn)
142                 {
143                         
144                         var iter = projects.map_iterator();
145                         while (iter.next()) {
146                                 if (iter.get_value().fn == fn) {
147                                         return iter.get_value();
148                                 }
149                                 
150                         }
151                         
152                         return null;
153                 
154                 }
155                 
156                 // load project data from project file.
157                 public static void   factoryFromFile(string jsonfile)
158                 {
159                          
160                         GLib.debug("parse %s", jsonfile);
161
162                         var pa = new Json.Parser();
163                         pa.load_from_file(jsonfile);
164                         var node = pa.get_root();
165
166                         
167                         if (node == null || node.get_node_type () != Json.NodeType.OBJECT) {
168                                 GLib.debug("SKIP " + jsonfile + " - invalid format?");
169                                 return;
170                         }
171                         
172                         var obj = node.get_object ();
173                         var xtype =  obj.get_string_member("xtype");
174
175
176                         var paths = obj.get_object_member("paths");
177                         var i = 0;
178                         var fpath = "";
179                         paths.foreach_member((sobj, key, val) => {
180                                 if (i ==0 ) {
181                                         fpath = key;
182                                 }
183                                         
184                         });
185
186                         
187                         var proj = factory(xtype, fpath);
188
189                         proj.json_project_data  = obj; // store the original object...
190                         
191                         proj.fn =  Path.get_basename(jsonfile).split(".")[0];
192
193                         // might not exist?
194
195                         if (obj.has_member("runhtml")) {
196                                         proj.runhtml  = obj.get_string_member("runhtml"); 
197                         }
198                         // might not exist?
199                         if (obj.has_member("base_template")) {
200                                         proj.base_template  = obj.get_string_member("base_template"); 
201                         }
202                         // might not exist?
203                         if (obj.has_member("rootURL")) {
204                                         proj.rootURL  = obj.get_string_member("rootURL"); 
205                         }
206                         
207                         proj.name = obj.get_string_member("name");
208
209                          
210                         paths.foreach_member((sobj, key, val) => {
211                                 proj.paths.set(key, "dir");
212                         });
213                         proj.initRooDatabase();
214                         
215                         GLib.debug("Add Project %s", proj.id);
216                         
217                         projects.set(proj.id,proj);
218                         
219                         
220                         
221                         
222                 }
223                 
224                 
225                 public static Project factory(string xtype, string path)
226                 {
227
228                         // check to see if it's already loaded..
229
230                          
231                         var iter = projects.map_iterator();
232                         while (iter.next()) {
233                                 if (iter.get_value().hasPath( path)) {
234                                         return iter.get_value();
235                                  }
236                         }
237
238
239                         switch(xtype) {
240                                 case "Gtk":
241                                         return new Gtk(path);
242                                 case "Roo":
243                                         return new Roo(path);
244                         }
245                         throw new Error.INVALID_TYPE("invalid project type");
246                                 
247                 }
248                 
249                 
250                 
251                  public static void  remove(Project project)
252                 {
253                         // delete the file..
254                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
255                                  
256                         FileUtils.unlink(dirname + "/" + project.fn + ".json");
257                         projects.unset(project.id,null);
258                         
259
260                 }
261                  
262
263                 public void save()
264                 {
265                                 // fixme..
266                         
267                         if (this.fn.length < 1) {
268                                 // make the filename..
269                                 //var t = new DateTime.now_local ();
270                                 //TimeVal tv;
271                                 //t.to_timeval(out tv);
272                                 //var str = "%l:%l".printf(tv.tv_sec,tv.tv_usec);
273                                 var str = this.firstPath();
274                                 
275                                         this.fn = GLib.Checksum.compute_for_string(GLib.ChecksumType.MD5, str, str.length);
276                         }
277
278                         var dirname = GLib.Environment.get_home_dir() + "/.Builder";
279                         var  s =  this.toJSON(false);
280                         FileUtils.set_contents(dirname + "/" + this.fn + ".json", s, s.length);  
281                         
282                         
283                 }
284
285                 
286                 
287                 public string toJSON(bool show_all)
288                 {
289                         
290                         
291                         this.json_project_data.set_string_member("name", this.name);
292                         this.json_project_data.set_string_member("fn", this.fn);
293                         this.json_project_data.set_string_member("xtype", this.xtype);
294                         this.json_project_data.set_string_member("runhtml", this.runhtml);
295                         this.json_project_data.set_string_member("rootURL", this.rootURL);
296                         this.json_project_data.set_string_member("base_template", this.base_template);
297                         this.json_project_data.set_string_member("rootURL", this.rootURL);
298  
299                         var paths = new Json.Object(); 
300
301
302                         var iter = this.paths.map_iterator();
303                         while (iter.next()) {
304                                 paths.set_string_member(iter.get_key(), "path");
305                         }
306                         this.json_project_data.set_object_member("paths", paths);
307
308                         
309                         if (show_all) {
310                                 var files = new Json.Array();
311                                 
312                                 
313                                 var fiter = this.files.map_iterator();
314                                 while (fiter.next()) {
315                                         files.add_string_element (fiter.get_key());
316                                 }
317                                 this.json_project_data.set_array_member("files", files);
318                                 
319                         }
320
321                 
322                         var  generator = new Json.Generator ();
323                         var  root = new Json.Node(Json.NodeType.OBJECT);
324                         root.init_object(this.json_project_data);
325                         generator.set_root (root);
326                         if (show_all) {
327                                 generator.pretty = true;
328                                 generator.indent = 4;
329                         }
330
331                         return  generator.to_data (null);
332                           
333                           
334                 }
335                 public string firstPath()
336                 {
337                         var iter = this.paths.map_iterator();
338                         while (iter.next()) {
339                                 return iter.get_key();
340                         }
341                   
342                         return "";
343                 }
344
345                 public bool hasPath(string path)
346                 {
347                         var iter = this.paths.map_iterator();
348                         while (iter.next()) {
349                                 if (iter.get_key() == path) {
350                                 return true;
351                         }
352                         }
353                   
354                         return false;
355                 }
356
357                 
358                 // returns the first path
359                 public string getName()
360                 {
361                         var iter = this.paths.map_iterator();
362                         while (iter.next()) {
363                                 return GLib.Path.get_basename(iter.get_key());
364                         }
365                   
366                         return "";
367                 }
368
369                 public Gee.ArrayList<JsRender.JsRender> sortedFiles()
370                 {
371                         var files = new Gee.ArrayList<JsRender.JsRender>();
372
373                         var fiter = this.files.map_iterator();
374                         while(fiter.next()) {
375                                 files.add(fiter.get_value());
376                         }
377                         files.sort((fa,fb) => {
378                                 return ((JsRender.JsRender)fa).name.collate(((JsRender.JsRender)fb).name);
379
380                         });
381                         return files;
382
383                 }
384                 
385          
386                 public JsRender.JsRender? getByName(string name)
387                 {
388                         
389                         var fiter = files.map_iterator();
390                         while(fiter.next()) {
391                          
392                                 var f = fiter.get_value();
393                                 
394                                 
395                                 GLib.debug ("Project.getByName: %s ?= %s" ,f.name , name);
396                                 if (f.name == name) {
397                                         return f;
398                                 }
399                         };
400                         return null;
401                 }
402                 public JsRender.JsRender? getByPath(string path)
403                 {
404                         
405                         var fiter = files.map_iterator();
406                         while(fiter.next()) {
407                          
408                                 var f = fiter.get_value();
409                                 
410                                 
411                                 //GLib.debug ("Project.getByName: %s ?= %s" ,f.name , name);
412                                 if (f.path == path) {
413                                         return f;
414                                 }
415                         };
416                         return null;
417                 }
418                 
419                 public JsRender.JsRender? getById(string id)
420                 {
421                         
422                         var fiter = files.map_iterator();
423                         while(fiter.next()) {
424                          
425                                 var f = fiter.get_value();
426                                 
427                                 
428                                 //console.log(f.id + '?=' + id);
429                                 if (f.id == id) {
430                                         return f;
431                                 }
432                                 };
433                         return null;
434                 }
435
436                 public JsRender.JsRender newFile (string name)
437                 {
438                         var ret =  JsRender.JsRender.factory(this.xtype, 
439                                                                                          this, 
440                                                                                          this.firstPath() + "/" + name + ".bjs");
441                         this.addFile(ret);
442                         return ret;
443                 }
444                 
445                 public JsRender.JsRender loadFileOnly (string path)
446                 {
447                         var xt = this.xtype;
448                         return JsRender.JsRender.factory(xt, this, path);
449                         
450                 }
451                 
452                 public JsRender.JsRender create(string filename)
453                 {
454                         var ret = this.loadFileOnly(filename);
455                         ret.save();
456                         this.addFile(ret);
457                         return ret;
458                         
459                 }
460                         
461                          
462                 public void addFile(JsRender.JsRender pfile) { // add a single file, and trigger changed.
463                 
464                 
465                         this.files.set(pfile.path, pfile); // duplicate check?
466                         this.on_changed();
467                 }
468                 
469                 public void add(string path, string type)
470                 {
471                         this.paths.set(path,type);
472                         //Seed.print(" type is '" + type + "'");
473                         if (type == "dir") {
474                                 this.scanDir(path);
475                         //    console.dump(this.files);
476                         }
477                         if (type == "file" ) {
478                         
479                                 this.files.set(path,this.loadFileOnly( path ));
480                         }
481                         this.on_changed();
482                         
483                 }
484                 public void  scanDirs() // cached version
485                 {
486                         if (this.is_scanned) {
487                                 return;
488                         }
489                         this.scanDirsForce();
490                         //console.dump(this.files);
491                         
492                 }
493                 
494                 public void  scanDirsForce()
495                 {
496                         this.is_scanned = true;  
497                         var iter = this.paths.map_iterator();
498                         while (iter.next()) {
499                                 //print("path: " + iter.get_key() + " : " + iter.get_value() +"\n");
500                                 if (iter.get_value() != "dir") {
501                                         continue;
502                                 }
503                                 this.scanDir(iter.get_key());
504                         }
505                         //console.dump(this.files);
506                         
507                 }
508                         // list files.
509                 public void scanDir(string dir, int dp =0 ) 
510                 {
511                         //dp = dp || 0;
512                         //print("Project.Base: Running scandir on " + dir +"\n");
513                         if (dp > 5) { // no more than 5 deep?
514                                 return;
515                         }
516                         // this should be done async -- but since we are getting the proto up ...
517                         
518                         var subs = new GLib.List<string>();;            
519                         var f = File.new_for_path(dir);
520                         try {
521                                 var file_enum = f.enumerate_children(GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null);
522                                 
523                                  
524                                 FileInfo next_file; 
525                                 while ((next_file = file_enum.next_file(null)) != null) {
526                                         var fn = next_file.get_display_name();
527                         
528                                          
529                                         //print("trying"  + dir + "/" + fn +"\n");
530                                         
531                                         if (fn[0] == '.') { // skip hidden
532                                                 continue;
533                                         }
534                                         
535                                         if (FileUtils.test(dir  + "/" + fn, GLib.FileTest.IS_DIR)) {
536                                                 subs.append(dir  + "/" + fn);
537                                                 continue;
538                                         }
539                                         
540                                         if (!Regex.match_simple("\\.bjs$", fn)) {
541                                                 //print("no a bjs\n");
542                                                 continue;
543                                         }
544                                         /*
545                                         var parent = "";
546                                         //if (dp > 0 ) {
547                                         
548                                         var sp = dir.split("/");
549                                         var parent = "";
550                                         for (var i = 0; i < sp.length; i++) {
551                                                 
552                                         }
553                                         
554                                         /*
555                                         sp = sp.splice(sp.length - (dp +1), (dp +1));
556                                         parent = sp.join('.');
557                                         
558                                         
559                                         if (typeof(_this.files[dir  + '/' + fn]) != 'undefined') {
560                                                 // we already have it..
561                                                 _this.files[dir  + '/' + fn].parent = parent;
562                                                 return;
563                                         }
564                                         */
565                                         var xt = this.xtype;
566                                         var el = JsRender.JsRender.factory(xt,this, dir + "/" + fn);
567                                         this.files.set( dir + "/" + fn, el);
568                                         // parent ?? 
569                                         
570                                          
571                                 }
572                         } catch (Error e) {
573                                 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
574                         } catch (GLib.Error e) {
575                                 GLib.warning("Project::scanDirs failed : " + e.message + "\n");
576                         }
577                         for (var i = 0; i < subs.length(); i++) {
578                                 
579                                  this.scanDir(subs.nth_data(i), dp+1);
580                         }
581                         
582                 }
583                 // wrapper around the javascript data...
584                 public string get_string_member(string key) {
585                         
586                         if (!this.json_project_data.has_member(key)) {
587                                 return "";
588                         }
589                         var  ret = this.json_project_data.get_string_member(key);
590                         if (ret == null) {
591                                 return "";
592                         }
593                         return ret;
594                         
595                 }
596                   
597         }
598 }
599