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