Fix #6904 - JSON output for docs / editor
[roojspacker] / roojspacker / DocTag.vala
1
2
3 namespace JSDOC 
4 {
5         public enum DocTagTitle
6         {
7                 NO_VALUE,
8                 PARAM,
9                 PROPERTY,
10                 CFG,
11                 EXAMPLE,
12                 SINGLETON,
13                 AUTHOR,
14                 METHOD,
15                 DESC,
16                 OVERVIEW,
17                 SINCE,
18                 CONSTANT,
19                 VERSION,
20                 DEPRECATED,
21  
22                 SEE,
23                 CLASS,
24                 NAMESPACE,
25                 CONSTRUCTOR,
26                 STATIC,
27  
28                 
29                 INNER,
30                 FIELD,
31                 FUNCTION,
32                 EVENT,
33                 NAME,
34                 RETURN,
35                 THROWS,
36                 REQUIRES,
37                 TYPE,
38                 PRIVATE,
39                 IGNORE,
40                 ARGUMENTS,
41                 EXTENDS,
42                 DEFAULT,
43                 MEMBEROF,
44                 PUBLIC,
45                 SCOPE,
46                 SCOPEALIAS,
47                 
48                 // these are some we have added for creating trees etc..
49                 CHILDREN, // what classes can be added as child in a tree
50                 PARENT,  // restrict what the class can be added to.
51                 ABSTRACT, // is the class abstract
52                 BUILDER_TOP // can the element be used as a top level in the gui builder
53   
54   
55         }
56         
57         errordomain DocTagException {
58                 NO_TITLE,
59                 INVALID_TITLE,
60                 INVALID_NAME,
61                 INVALID_TYPE
62         }
63
64
65         public class DocTag : Object 
66         {
67
68                 public DocTagTitle title = DocTagTitle.NO_VALUE;
69                 public string type = "";  // eg.. boolean / string etc..., may be xxxx|bbbb - eg. optional types
70                 public string name = ""; // eg. "title" << a property name etc...
71                 public bool isOptional = false;
72                 public string defaultValue = "";
73                 public string desc = "";
74                 public Gee.ArrayList<string> optvalues;
75                 public string memberOf = ""; // set by add addMember..
76
77                 public string asString()
78                 {
79                         return "DocTag: title=%s name=%s type=%s  desc=%s".printf(
80                                 this.title.to_string(),
81                                 this.name,
82                                 this.type,
83                                 this.desc
84                         );
85                 }
86         
87                 public Json.Object toJson()
88                 {
89                         var ret = new Json.Object();
90                         ret.set_string_member("title", this.title.to_string());
91                         ret.set_string_member("type", this.type);
92                         ret.set_string_member("name", this.name);
93                         ret.set_string_member("defaultValue", this.defaultValue);
94                         ret.set_string_member("desc", this.desc);
95                         ret.set_string_member("memberOf", this.memberOf);
96                         ret.set_boolean_member("isOptional", this.isOptional);
97                         var ar = new Json.Array();
98                         foreach(var ov in this.optvalues) {
99                                 ar.add_string_element(ov);
100                         }
101                         ret.set_array_member("optvalues", ar);
102                         return ret;
103                 }
104         
105         
106                 public DocTag (string in_src)
107                 {
108                     
109                     GLib.debug("Parsing Tag: %s", in_src);
110                     
111                      
112                     
113                     
114                     this.optvalues = new Gee.ArrayList<string>();
115                     
116                     var src = in_src;
117                         
118             try {
119                 src = this.nibbleTitle(src);
120                 
121                 src = this.nibbleType(src);
122                 
123
124                 // only some tags are allowed to have names.
125                 if (
126                         this.title == DocTagTitle.PARAM ||
127                         this.title == DocTagTitle.PROPERTY || 
128                         this.title == DocTagTitle.CFG) { // @config is deprecated
129                     src = this.nibbleName(src);
130                 }
131             }
132             catch(DocTagException e) {
133                 GLib.debug("Failed to parse tag: '%s' = error = %s", in_src, e.message);
134                 // only throw if in 'strict'??
135                 //throw e;
136                 return;
137             }
138             
139             // if type == @cfg, and matches (|....|...)
140             
141             src = src.strip();
142  
143             MatchInfo mi = null;
144             
145             if (this.title ==  DocTagTitle.CFG && /^\([^)]+\)/.match_all(src, 0, out mi )) {
146                     
147                                 var ms = mi.fetch(0);
148                                 GLib.debug("Got Opt list: %s", ms);
149                                 
150                                 ms = ms.substring(1,ms.length-2);
151                                 if (ms.contains("|")) {
152                                         var ar = ms.split("|");
153                                         for (var i =0 ; i < ar.length;i++) {
154                                                 optvalues.add(ar[i].strip());
155                                         }
156                                         src = src.substring(ms.length, src.length - ms.length);                   
157                     
158                 } 
159                 
160             }
161             
162             
163             this.desc = src; // whatever is left
164             
165             // example tags need to have whitespace preserved
166             if (this.title != DocTagTitle.EXAMPLE) {
167                         this.desc = this.desc.strip();
168                 }
169             
170
171                 
172         
173
174                 }
175         
176         
177                 /**
178                     Find and shift off the title of a tag.
179                     @param {string} src
180                     @return src
181                  */
182                 private string nibbleTitle (string src) throws DocTagException
183                 {
184                     //GLib.debug("nibbleTitle: %s", src);
185                     MatchInfo mi;
186                      
187                     if(! /^\s*(\S+)\s*(?:\s([\s\S]*))?$/.match_full(src, src.length, 0, 0, out mi) || 
188                             mi.get_match_count() < 2)  {
189                                 throw new DocTagException.NO_TITLE("missing title");
190                                 return src;
191                     }
192                     
193                     // convert the @xxx to a DocTagTitle
194                     // wonder if caching this as a GeeHashmap would be quicker?
195                     
196                     EnumClass enumc = (EnumClass) typeof (DocTagTitle).class_ref ();
197
198                     unowned EnumValue? eval = enumc.get_value_by_name(
199                         //       "JSDOC_DOC_TAG_TITLE_"+  mi.fetch(1).up()
200                                  "JSDOC_DOC_TAG_TITLE_"+  mi.fetch(1).up().replace("-", "_")
201                  );
202                     if (eval == null) {
203                                 throw new DocTagException.INVALID_TITLE("title not supported ??");
204                                 return src;
205                     }
206                     this.title = (DocTagTitle) eval.value;
207                     return mi.get_match_count() > 2 ? mi.fetch(2) : "";
208
209                 }
210                  
211                   /**
212             Find and shift off the type of a tag.
213             @requires frame/String.js
214             @param {string} src
215             @return src
216          */
217         private string nibbleType(string src) 
218         {
219                     MatchInfo mi;
220             if(! /^\s*\{/.match_all(src, 0, out mi)) {
221                    return src;
222             }
223             int start;
224             int stop;
225               
226                         this.balance(src,'{', '}', out start, out stop);
227                         //GLib.debug("nibble type: %s %d, %d", src, start,stop);
228             if (stop == -1) {
229                 throw new DocTagException.INVALID_TYPE("Malformed comment tag ignored. Tag type requires an opening { and a closing }: ") ;
230                 return src;
231             }
232             this.type = src.substring(start+1,stop-1).strip();
233             this.type = this.type.replace(",", "|"); // multiples can be separated by , or |
234             return src.substring(stop+1, -1);
235             
236         }
237          
238          
239          
240         /**
241             Find and shift off the name of a tag.
242             @requires frame/String.js
243             @param {string} src
244             @return src
245          */
246                 private string nibbleName( string in_src) throws DocTagException
247         {
248
249            
250             var src = in_src.strip();
251             //GLib.debug("nibbleName: %s", in_src);
252             
253             // is optional?
254             if (src.get(0) == '[') {
255                         int start, stop;
256                  this.balance(src,'[', ']', out start, out stop);
257                 if (stop == -1) {
258                     throw new  DocTagException.INVALID_NAME("Malformed comment tag ignored. Tag optional name requires an opening [ and a closing ]: ");
259                     return src;
260                 }
261                 this.name = src.substring(start+1, stop).strip();
262                 this.isOptional = true;
263                 
264                 src = src.substring(stop+1);
265                 
266                 // has default value?
267                 var nameAndValue = this.name.split("=");
268                 if (nameAndValue.length > 1) {
269                         var oname = this.name;
270                     this.name = nameAndValue[0].strip();
271
272                     this.defaultValue = oname.substring( nameAndValue[0].length + 1 , nameAndValue[0].length + 1 - oname.length); /// what about
273                 }
274                 GLib.debug("got name %s", this.name);                
275                 return src.substring(stop+1, stop+1-src.length);
276             }
277                         // not encased with [ ]
278
279                     MatchInfo mi;
280
281             if (/^(\S+)(?:\s([\s\S]*))?$/.match_full(src, src.length, 0, 0,  out mi)) {
282                         this.name = mi.fetch(1);
283                         GLib.debug("got name %s", this.name);
284                                 return mi.get_match_count() > 2 ? mi.fetch(2) : "";
285             }
286                 
287
288             return src;
289         }
290          
291          
292         private void balance(string str, char open, char close, out int start, out int stop) {
293             start = 0;
294             stop  =-1;
295             while (str.get(start) != open) {
296                 if (start == str.length) {
297                         return;
298                         }
299                 start++;
300             }
301             
302             stop = start +1;
303             var balance = 1;
304             while (stop < str.length) {
305                 if (str.get(stop) == open) balance++;
306                 if (str.get(stop) == close) balance--;
307                 if (balance == 0) break;
308                 stop++;
309                 if (stop == str.length) {
310                         stop = -1;
311                         return;
312                         }
313             }
314             
315
316                 }
317                 
318                 public Json.Array optvalue_as_json_array()
319                 {
320                         var ret = new Json.Array();
321                         foreach (var str in this.optvalues ) {
322                                 ret.add_string_element(str);
323                         }
324                         return ret;
325                         
326                         
327                 }
328                 public Json.Object toPropertyJSON (Symbol parent)
329                 {
330                         
331                         var add = new Json.Object();
332                         add.set_string_member("name",this.name);
333                         add.set_string_member("type",this.type);
334                         add.set_string_member("desc",this.desc);
335                         add.set_string_member("memberOf", this.memberOf == parent.alias ? "" : this.memberOf);
336                     return add;
337             }   
338                 
339                 
340         }
341 }
342         
343