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