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