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