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                         var builder = new Json.Builder ();
246
247                         builder.begin_object ();
248
249                         builder.set_member_name ("name");
250                         builder.add_string_value (this.name);
251
252
253                         builder.set_member_name ("fn");
254                         builder.add_string_value (this.fn);
255
256                         builder.set_member_name ("xtype");
257                         builder.add_string_value (this.xtype);
258
259                         builder.set_member_name ("runhtml");
260                         builder.add_string_value (this.runhtml);
261
262
263                         builder.set_member_name ("rootURL");
264                         builder.add_string_value (this.rootURL);
265                         
266                         builder.set_member_name ("base_template");
267                         builder.add_string_value (this.base_template);                  
268                         // file ??? try/false?
269                         builder.set_member_name ("paths");
270
271
272                         builder.begin_object ();
273
274
275                         var iter = this.paths.map_iterator();
276                         while (iter.next()) {
277                                 builder.set_member_name (iter.get_key());
278                                 builder.add_string_value("path");
279                         }
280                         builder.end_object ();
281                         
282                         if (show_all) {
283                                 builder.set_member_name ("files");
284                                 builder.begin_array ();
285                                 var fiter = this.files.map_iterator();
286                                 while (fiter.next()) {
287                                     builder.add_string_value (fiter.get_key());
288                                 }
289                                 
290                                 
291                                 builder.end_array ();
292                         }
293
294                 
295                         builder.end_object ();
296
297                         var  generator = new Json.Generator ();
298                         var  root = builder.get_root ();
299                         generator.set_root (root);
300                         if (show_all) {
301                                 generator.pretty = true;
302                                 generator.indent = 4;
303                         }
304
305                         return  generator.to_data (null);
306                           
307                       
308                 }
309                 public string firstPath()
310                 {
311                     var iter = this.paths.map_iterator();
312                     while (iter.next()) {
313                         return iter.get_key();
314                     }
315                   
316                     return "";
317                 }
318
319                 public bool hasPath(string path)
320                 {
321                     var iter = this.paths.map_iterator();
322                     while (iter.next()) {
323                         if (iter.get_key() == path) {
324                                 return true;
325                         }
326                     }
327                   
328                     return false;
329                 }
330
331                 
332                 // returns the first path
333                 public string getName()
334                 {
335                     var iter = this.paths.map_iterator();
336                     while (iter.next()) {
337                         return GLib.Path.get_basename(iter.get_key());
338                     }
339                   
340                     return "";
341                 }
342
343                 public Gee.ArrayList<JsRender.JsRender> sortedFiles()
344                 {
345                         var files = new Gee.ArrayList<JsRender.JsRender>();
346
347                         var fiter = this.files.map_iterator();
348                         while(fiter.next()) {
349                                 files.add(fiter.get_value());
350                         }
351                         files.sort((fa,fb) => {
352                                 return ((JsRender.JsRender)fa).name.collate(((JsRender.JsRender)fb).name);
353
354                         });
355                         return files;
356
357                 }
358                 
359          
360                 public JsRender.JsRender? getByName(string name)
361                 {
362                     
363                         var fiter = files.map_iterator();
364                     while(fiter.next()) {
365                      
366                         var f = fiter.get_value();
367                         
368                         
369                         print ("Project.getByName: %s ?= %s\n" ,f.name , name);
370                         if (f.name == name) {
371                             return f;
372                         }
373                     };
374                     return null;
375                 }
376                 
377                 public JsRender.JsRender? getById(string id)
378                 {
379                     
380                         var fiter = files.map_iterator();
381                         while(fiter.next()) {
382                      
383                                 var f = fiter.get_value();
384                                 
385                                 
386                                 //console.log(f.id + '?=' + id);
387                                 if (f.id == id) {
388                                     return f;
389                                 }
390                             };
391                         return null;
392                 }
393
394                 public JsRender.JsRender newFile (string name)
395                 {
396                         var ret =  JsRender.JsRender.factory(this.xtype, 
397                                                          this, 
398                                                          this.firstPath() + "/" + name + ".bjs");
399                         this.addFile(ret);
400                         return ret;
401                 }
402                 
403                 public JsRender.JsRender loadFileOnly (string path)
404                 {
405                     var xt = this.xtype;
406                     return JsRender.JsRender.factory(xt, this, path);
407                     
408                 }
409                 
410                 public JsRender.JsRender create(string filename)
411                 {
412                     var ret = this.loadFileOnly(filename);
413                     ret.save();
414                     this.addFile(ret);
415                     return ret;
416                     
417                 }
418                     
419                      
420                 public void addFile(JsRender.JsRender pfile) { // add a single file, and trigger changed.
421                 
422                 
423                     this.files.set(pfile.path, pfile); // duplicate check?
424                     this.on_changed();
425                 }
426                 
427                 public void add(string path, string type)
428                 {
429                     this.paths.set(path,type);
430                     //Seed.print(" type is '" + type + "'");
431                     if (type == "dir") {
432                         this.scanDir(path);
433                     //    console.dump(this.files);
434                     }
435                     if (type == "file" ) {
436                         
437                         this.files.set(path,this.loadFileOnly( path ));
438                     }
439                     this.on_changed();
440                     
441                 }
442                 public void  scanDirs() // cached version
443                 {
444                     if (this.is_scanned) {
445                                 return;
446                         }
447                         this.scanDirsForce();
448                     //console.dump(this.files);
449                     
450                 }
451                 
452                 public void  scanDirsForce()
453                 {
454                         this.is_scanned = true;  
455                         var iter = this.paths.map_iterator();
456                     while (iter.next()) {
457                                 //print("path: " + iter.get_key() + " : " + iter.get_value() +"\n");
458                         if (iter.get_value() != "dir") {
459                             continue;
460                         }
461                         this.scanDir(iter.get_key());
462                     }
463                     //console.dump(this.files);
464                     
465                 }
466                     // list files.
467                 public void scanDir(string dir, int dp =0 ) 
468                 {
469                     //dp = dp || 0;
470                     //print("Project.Base: Running scandir on " + dir +"\n");
471                     if (dp > 5) { // no more than 5 deep?
472                         return;
473                     }
474                     // this should be done async -- but since we are getting the proto up ...
475                     
476                     var subs = new GLib.List<string>();;            
477                     var f = File.new_for_path(dir);
478                     try {
479                         var file_enum = f.enumerate_children(GLib.FileAttribute.STANDARD_DISPLAY_NAME, GLib.FileQueryInfoFlags.NONE, null);
480                         
481                          
482                         FileInfo next_file; 
483                         while ((next_file = file_enum.next_file(null)) != null) {
484                             var fn = next_file.get_display_name();
485                     
486                              
487                             //print("trying"  + dir + "/" + fn +"\n");
488                             
489                             if (fn[0] == '.') { // skip hidden
490                                 continue;
491                             }
492                             
493                             if (FileUtils.test(dir  + "/" + fn, GLib.FileTest.IS_DIR)) {
494                                 subs.append(dir  + "/" + fn);
495                                 continue;
496                             }
497                             
498                             if (!Regex.match_simple("\\.bjs$", fn)) {
499                                                 //print("no a bjs\n");
500                                 continue;
501                             }
502                             /*
503                             var parent = "";
504                             //if (dp > 0 ) {
505                             
506                             var sp = dir.split("/");
507                             var parent = "";
508                             for (var i = 0; i < sp.length; i++) {
509                                 
510                             }
511                             
512                             /*
513                             sp = sp.splice(sp.length - (dp +1), (dp +1));
514                             parent = sp.join('.');
515                             
516                             
517                             if (typeof(_this.files[dir  + '/' + fn]) != 'undefined') {
518                                 // we already have it..
519                                 _this.files[dir  + '/' + fn].parent = parent;
520                                 return;
521                             }
522                             */
523                             var xt = this.xtype;
524                                         var el = JsRender.JsRender.factory(xt,this, dir + "/" + fn);
525                             this.files.set( dir + "/" + fn, el);
526                             // parent ?? 
527                             
528                              
529                         }
530                     } catch (Error e) {
531                         print("Project::scanDirs failed : " + e.message + "\n");
532                     } catch (GLib.Error e) {
533                                 print("Project::scanDirs failed : " + e.message + "\n");
534                         }
535                         for (var i = 0; i < subs.length(); i++) {
536                         
537                          this.scanDir(subs.nth_data(i), dp+1);
538                     }
539                     
540                 }
541                   
542         }
543 }
544