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