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