49545a67a86d17f278dbacd54ebe26124e8c3fe6
[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         }
49         
50         errordomain DocTagException {
51                 NO_TITLE,
52                 INVALID_TITLE,
53                 INVALID_NAME,
54                 INVALID_TYPE
55         }
56
57
58         public class DocTag : Object 
59         {
60
61                 public DocTagTitle title = DocTagTitle.NO_VALUE;
62                 public string type = "";  // eg.. boolean / string etc..., may be xxxx|bbbb - eg. optional types
63                 public string name = ""; // eg. "title" << a property name etc...
64                 public bool isOptional = false;
65                 public string defaultValue = "";
66                 public string desc = "";
67                 public Gee.ArrayList<string> optvalues;
68                 public string memberOf = ""; // set by add addMember..
69
70                 public string asString()
71                 {
72                         return "DocTag: title=%s name=%s type=%s  desc=%s".printf(
73                                 this.title.to_string(),
74                                 this.name,
75                                 this.type,
76                                 this.desc
77                         );
78                 }
79         
80  
81                          
82         
83         
84                 public DocTag (string in_src)
85                 {
86                     
87                     GLib.debug("Parsing Tag: %s", in_src);
88                     
89                      
90                     
91                     
92                     this.optvalues = new Gee.ArrayList<string>();
93                     
94                     var src = in_src;
95                         
96             try {
97                 src = this.nibbleTitle(src);
98                 
99                 src = this.nibbleType(src);
100                 
101
102                 // only some tags are allowed to have names.
103                 if (
104                         this.title == DocTagTitle.PARAM ||
105                         this.title == DocTagTitle.PROPERTY || 
106                         this.title == DocTagTitle.CFG) { // @config is deprecated
107                     src = this.nibbleName(src);
108                 }
109             }
110             catch(DocTagException e) {
111                 GLib.debug("Failed to parse tag: '%s' = error = %s", in_src, e.message);
112                 // only throw if in 'strict'??
113                 //throw e;
114                 return;
115             }
116             
117             // if type == @cfg, and matches (|....|...)
118             
119             src = src.strip();
120  
121             MatchInfo mi = null;
122             
123             if (this.title ==  DocTagTitle.CFG && /^\([^)]+\)/.match_all(src, 0, out mi )) {
124                                 var ms = mi.fetch(0);
125                                 if (ms.contains("|")) {
126                                         var ar = ms.split("|");
127                                         for (var i =0 ; i < ar.length;i++) {
128                                                 optvalues.add(ar[i].strip());
129                                         }
130                                         src = src.substring(ms.length, src.length - ms.length);                   
131                     
132                 } 
133                 
134             }
135             
136             
137             this.desc = src; // whatever is left
138             
139             // example tags need to have whitespace preserved
140             if (this.title != DocTagTitle.EXAMPLE) {
141                         this.desc = this.desc.strip();
142                 }
143             
144
145                 
146         
147
148                 }
149         
150         
151                 /**
152                     Find and shift off the title of a tag.
153                     @param {string} src
154                     @return src
155                  */
156                 private string nibbleTitle (string src) throws DocTagException
157                 {
158                     //GLib.debug("nibbleTitle: %s", src);
159                     MatchInfo mi;
160                      
161                     if(! /^\s*(\S+)\s*(?:\s([\s\S]*))?$/.match_full(src, src.length, 0, 0, out mi) || 
162                             mi.get_match_count() < 2)  {
163                                 throw new DocTagException.NO_TITLE("missing title");
164                                 return src;
165                     }
166                     
167                     //GLib.debug("nibbleTitle: regexmatches %d : %s",
168                     //           mi.get_match_count(), 
169                     //           mi.fetch(1).up());
170                     
171                     EnumClass enumc = (EnumClass) typeof (DocTagTitle).class_ref ();
172
173                     unowned EnumValue? eval = enumc.get_value_by_name ( "JSDOC_DOC_TAG_TITLE_"+  mi.fetch(1).up());
174                     if (eval == null) {
175                                 throw new DocTagException.INVALID_TITLE("title not supported ??");
176                                 return src;
177                     }
178                     this.title = (DocTagTitle) eval.value;
179                     return mi.get_match_count() > 2 ? mi.fetch(2) : "";
180
181                 }
182                  
183                   /**
184             Find and shift off the type of a tag.
185             @requires frame/String.js
186             @param {string} src
187             @return src
188          */
189         private string nibbleType(string src) 
190         {
191                     MatchInfo mi;
192             if(! /^\s*\{/.match_all(src, 0, out mi)) {
193                    return src;
194             }
195             int start;
196             int stop;
197               
198                         this.balance(src,'{', '}', out start, out stop);
199                         //GLib.debug("nibble type: %s %d, %d", src, start,stop);
200             if (stop == -1) {
201                 throw new DocTagException.INVALID_TYPE("Malformed comment tag ignored. Tag type requires an opening { and a closing }: ") ;
202                 return src;
203             }
204             this.type = src.substring(start+1,stop-1).strip();
205             this.type = this.type.replace(",", "|"); // multiples can be separated by , or |
206             return src.substring(stop+1, -1);
207             
208         }
209          
210          
211          
212         /**
213             Find and shift off the name of a tag.
214             @requires frame/String.js
215             @param {string} src
216             @return src
217          */
218                 private string nibbleName( string in_src) throws DocTagException
219         {
220
221            
222             var src = in_src.strip();
223             //GLib.debug("nibbleName: %s", in_src);
224             
225             // is optional?
226             if (src.get(0) == '[') {
227                         int start, stop;
228                  this.balance(src,'[', ']', out start, out stop);
229                 if (stop == -1) {
230                     throw new  DocTagException.INVALID_NAME("Malformed comment tag ignored. Tag optional name requires an opening [ and a closing ]: ");
231                     return src;
232                 }
233                 this.name = src.substring(start+1, stop).strip();
234                 this.isOptional = true;
235                 
236                 src = src.substring(stop+1);
237                 
238                 // has default value?
239                 var nameAndValue = this.name.split("=");
240                 if (nameAndValue.length > 1) {
241                         var oname = this.name;
242                     this.name = nameAndValue[0].strip();
243
244                     this.defaultValue = oname.substring( nameAndValue[0].length + 1 , nameAndValue[0].length + 1 - oname.length); /// what about
245                 }
246                 GLib.debug("got name %s", this.name);                
247                 return src.substring(stop+1, stop+1-src.length);
248             }
249                         // not encased with [ ]
250
251                     MatchInfo mi;
252
253             if (/^(\S+)(?:\s([\s\S]*))?$/.match_full(src, src.length, 0, 0,  out mi)) {
254                         this.name = mi.fetch(1);
255                         GLib.debug("got name %s", this.name);
256                                 return mi.get_match_count() > 2 ? mi.fetch(2) : "";
257             }
258                 
259
260             return src;
261         }
262          
263          
264         private void balance(string str, char open, char close, out int start, out int stop) {
265             start = 0;
266             stop  =-1;
267             while (str.get(start) != open) {
268                 if (start == str.length) {
269                         return;
270                         }
271                 start++;
272             }
273             
274             stop = start +1;
275             var balance = 1;
276             while (stop < str.length) {
277                 if (str.get(stop) == open) balance++;
278                 if (str.get(stop) == close) balance--;
279                 if (balance == 0) break;
280                 stop++;
281                 if (stop == str.length) {
282                         stop = -1;
283                         return;
284                         }
285             }
286             
287
288                 }
289                 
290                 public Json.Array optvalue_as_json_array()
291                 {
292                         var ret = new Json.Array();
293                         foreach (var str in this.optvalues ) {
294                                 ret.add_string_element(str);
295                         }
296                         return ret;
297                         
298                         
299                 }
300                 
301         }
302 }
303         
304