6074875043ea4f7a833796ce2ea59d0164a843e2
[potlatch2.git] / org / as3yaml / SafeConstructor.as
1 /* Copyright (c) 2007 Derek Wischusen
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy of 
4  * this software and associated documentation files (the "Software"), to deal in 
5  * the Software without restriction, including without limitation the rights to 
6  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
7  * of the Software, and to permit persons to whom the Software is furnished to do
8  * so, subject to the following conditions:
9  * 
10  * The above copyright notice and this permission notice shall be included in all
11  * copies or substantial portions of the Software.
12  * 
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
19  * SOFTWARE.
20  */
21
22 package org.as3yaml {
23
24 import flash.utils.Dictionary;
25 import flash.utils.getDefinitionByName;
26 import flash.utils.getQualifiedClassName;
27
28 import mx.utils.Base64Decoder;
29 import mx.utils.ObjectUtil;
30
31 import org.as3yaml.nodes.Node;
32 import org.idmedia.as3commons.util.ArrayList;
33 import org.idmedia.as3commons.util.Collection;
34 import org.idmedia.as3commons.util.HashMap;
35 import org.idmedia.as3commons.util.Map;
36 import org.as3yaml.util.StringUtils;
37
38 public class SafeConstructor extends BaseConstructor {
39     private static var yamlConstructors : Dictionary = new Dictionary();
40     private static var yamlMultiConstructors : Dictionary = new Dictionary();
41     private static var yamlMultiRegexps : Map = new HashMap();
42    
43     override public function getYamlConstructor(key:Object) : Function {
44         
45         var ctor : Function = yamlConstructors[key];
46         
47         if(ctor == null) {
48           ctor = super.getYamlConstructor(key);
49         }   
50         return ctor;
51     }
52
53     override public function getYamlMultiConstructor(key : Object) : Function {
54         
55         var ctor : Function = yamlMultiConstructors[key];
56         
57         if(ctor == null) {
58          ctor = super.getYamlMultiConstructor(key);
59         } 
60            
61         return ctor;
62     }
63
64     override public function getYamlMultiRegexp(key : Object) : RegExp {
65         var mine : RegExp = yamlMultiRegexps.get(key);
66         if(mine == null) {
67             mine = super.getYamlMultiRegexp(key);
68         }
69         return mine;
70     }
71
72     override public function getYamlMultiRegexps() : Map {
73         var all : Map = new HashMap();
74         all.putAll(super.getYamlMultiRegexps());
75         all.putAll(yamlMultiRegexps);
76         return all;
77     }
78
79     public static function addConstructor(tag : String, ctor : Function) : void {
80         yamlConstructors[tag] = ctor;
81     }
82
83     public static function addMultiConstructor(tagPrefix : String, ctor : Function) : void {
84         yamlMultiConstructors[tagPrefix] = ctor;
85         yamlMultiRegexps.put(tagPrefix, new RegExp("^"+tagPrefix));
86     }
87
88     public function SafeConstructor(composer : Composer) {
89         super(composer);
90     }
91
92     private static var BOOL_VALUES : Object = {
93                                                                                         yes     : true,
94                                                                                         no      : false,
95                                                                                         "true"  : true,
96                                                                                         "false" : false,
97                                                                                         on      : true,
98                                                                                         off     : false
99                                                                                   };
100
101     public static function constructYamlNull(ctor : Constructor, node : Node) : Object {
102         return null;
103     }
104     
105     public static function constructYamlBool(ctor : Constructor, node : Node) : Object {
106         var val : String = ctor.constructScalar(node) as String;
107         return BOOL_VALUES[val.toLowerCase()];
108     }
109
110     public static function constructYamlOmap(ctor : Constructor, node : Node) : Object {
111         return ctor.constructOmap(node);
112     }
113
114     public static function constructYamlPairs(ctor : Constructor, node : Node) : Object {
115         return ctor.constructPairs(node); 
116     }
117
118     public static function constructYamlSet(ctor : Constructor, node : Node) : Object {
119         return Map(ctor.constructMapping(node)).keySet();
120     }
121
122     public static function constructYamlStr(ctor : Constructor, node : Node) : Object {
123         var value : String = ctor.constructScalar(node) as String;
124         return value.length == 0 ? null : value;
125     }
126
127     public static function constructYamlSeq(ctor : Constructor, node : Node) : Object {
128         return ctor.constructSequence(node);
129     }
130
131     public static function constructYamlMap(ctor : Constructor, node : Node) : Object {
132         return ctor.constructMapping(node);
133     }
134
135     public static function constructUndefined(ctor : Constructor, node : Node) : Object {
136         throw new ConstructorException(null,"could not determine a constructor for the tag " + node.getTag(),null);
137     }
138
139     private static var TIMESTAMP_REGEXP : RegExp = new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:(?:[Tt]|[ \t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \t]*(?:Z|([-+][0-9][0-9]?)(?::([0-9][0-9])?)?))?)?$");
140     private static var YMD_REGEXP : RegExp = new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)$");
141     public static function constructYamlTimestamp(ctor : Constructor, node : Node) : Object {
142         var match : Object = YMD_REGEXP.exec(String(node.getValue()));
143
144         var year_s : String;
145         var month_s : String;
146         var day_s : String;
147         var time : Date = null;
148        
149         if(match) {
150             year_s = match[1];
151             month_s = match[2];
152             day_s = match[3];
153             
154             if(year_s == null || month_s == null || day_s == null) {
155                throw new ConstructorException(null, "bad date value: " + node.getValue(), null);
156             }
157
158             return new Date(year_s,int(month_s)-1,day_s);
159         }
160         match = TIMESTAMP_REGEXP.exec(String(node.getValue()));
161         if(!match) {
162             return ctor.constructPrivateType(node);
163         }
164         
165         year_s = match[1];
166         month_s = match[2];
167         day_s = match[3];
168         
169         var hour_s: String = match[4];
170         var min_s: String = match[5];
171         var sec_s: String = match[6];
172         var fract_s: String = match[7];
173         var timezoneh_s: String = match[8];
174         var timezonem_s: String = match[9];
175         
176         var usec : int = 0;
177         if(fract_s != null) {
178             usec = int(fract_s);
179             if(usec != 0) {
180                 while(10*usec < 1000) {
181                     usec *= 10;
182                 }
183             }
184         }
185         
186         time = new Date();
187         
188         if(month_s != null && day_s != null) {
189             time.setMonth(int(month_s)-1, day_s);
190         }      
191         if(year_s != null) {
192             time.setFullYear(year_s);
193         }
194         if(hour_s != null) {
195             time.setHours(hour_s);
196         }
197         if(min_s != null) {
198             time.setMinutes(min_s);
199         }
200         if(sec_s != null) {
201             time.setSeconds(sec_s);
202         }
203         time.setMilliseconds(usec);
204         if(timezoneh_s != null || timezonem_s != null) {
205             var zone : int = 0;
206             var sign : int = 1;
207             if(timezoneh_s != null) {
208                 if(timezoneh_s.charAt(0) == ("-")) {
209                     sign = -1;
210                 }
211                 zone += int(timezoneh_s.substring(1))*3600000;
212             }
213             if(timezonem_s != null) {
214                 zone += int(timezonem_s)*60000;
215             }
216         }
217         return time;
218     }
219
220     public static function constructYamlInt(ctor : Constructor, node : Node) : Object {
221         var value : String = String(ctor.constructScalar(node)).replace(/_/g,"");
222         var sign : int = +1;
223         var first : String = value.charAt(0);
224         if(first == '-') {
225             sign = -1;
226             value = value.substring(1);
227         } else if(first == '+') {
228             value = value.substring(1);
229         }
230         var base : int = 10;
231         if(value == ("0")) {
232             return 0;
233         } else if(StringUtils.startsWith(value, "0b")) {
234             value = value.substring(2);
235             base = 2;
236         } else if(StringUtils.startsWith(value, "0x")) {
237             value = value.substring(2);
238             base = 16;
239         } else if(value.charAt(0) == ("0")) {
240             value = value.substring(1);
241             base = 8;
242         } else if(value.indexOf(':') != -1) {
243             var digits : Array = value.split(":");
244             var bes : int = 1;
245             var val : int = 0;
246             for(var i:int=0,j:int=digits.length;i<j;i++) {
247                 val += (Number(digits[(j-i)-1])*bes);
248                 bes *= 60;
249             }
250             return new int(sign*val);
251         } else {
252             return sign * parseInt(value, base);//new Number(sign * int(value));
253         }
254         return (sign * parseInt(value, base));
255     }
256
257     private static var INF_VALUE_POS : Number  = new Number(Number.POSITIVE_INFINITY);
258     private static var INF_VALUE_NEG : Number = new Number(Number.NEGATIVE_INFINITY);
259     private static var NAN_VALUE : Number = new Number(Number.NaN);
260
261     public static function constructYamlFloat(ctor : Constructor, node : Node) : Object {
262         var value : String = String(ctor.constructScalar(node).toString()).replace(/'_'/g,"");
263         var sign : int = +1;
264         var first : String = value.charAt(0);
265         if(first == '-') {
266             sign = -1;
267             value = value.substring(1);
268         } else if(first == '+') {
269             value = value.substring(1);
270         }
271         var valLower : String = value.toLowerCase();
272         if(valLower == (".inf")) {
273             return sign == -1 ? INF_VALUE_NEG : INF_VALUE_POS;
274         } else if(valLower == (".nan")) {
275             return NAN_VALUE;
276         } else if(value.indexOf(':') != -1) {
277             var digits : Array = value.split(":");
278             var bes : int = 1;
279             var val : Number = 0.0;
280             for(var i:int=0,j:int=digits.length;i<j;i++) {
281                 val += (Number(digits[(j-i)-1])*bes);
282                 bes *= 60;
283             }
284             return new Number(sign*val);
285         } else {
286             return Number(value) * sign;
287         }
288     }    
289
290     public static function constructYamlBinary(ctor : Constructor, node : Node) : Object {
291         var values : Array = ctor.constructScalar(node).toString().split("[\n\u0085]|(?:\r[^\n])");
292         var vals : String = new String();
293         for(var i:int=0,j:int=values.length;i<j;i++) {
294             vals += (values[i]);
295         }
296         var decoder : Base64Decoder = new Base64Decoder();
297         decoder.decode(vals);
298         return decoder.flush();
299     }
300
301     public static function constructSpecializedSequence(ctor : Constructor, pref : String, node : Node) : Object {
302         var outp : ArrayList = null;
303         try {
304             var seqClass : Object = getDefinitionByName(pref) as Class;
305             outp = new seqClass() as ArrayList;
306         } catch(e : Error) {
307             throw new YAMLException("Can't construct a sequence from class " + pref + ": " + e.toString());
308         }
309         var coll : Collection = ctor.constructSequence(node) as Collection;
310         outp.addAll(coll);
311         return outp;
312     }
313
314     public static function constructSpecializedMap(ctor : Constructor, pref : String, node : Node) : Object {
315         var outp : Map = null;
316         try {
317             var mapClass : Class = getDefinitionByName(pref) as Class;
318             outp = new mapClass();
319         } catch(e : Error) {
320             throw new YAMLException("Can't construct a mapping from class " + pref + ": " + e.toString());
321         }
322         var coll : Map = ctor.constructMapping(node) as Map;
323         outp.putAll(coll);
324         return outp;
325     }
326
327     private static function fixValue(inp : Object, outp : Class) : Object {
328         if(inp == null) {
329             return null;
330         }
331         var inClass : Class = getDefinitionByName(getQualifiedClassName(inp)) as Class;
332         if(outp is inClass) {
333             return inp;
334         }
335         if(inClass == Number && (outp == int)) {
336             return new int(inp);
337         }
338
339         if(inClass == Number && (outp == String)) {
340             return new String(inp);
341         }
342
343         return inp;
344     }
345
346     public static function constructActionscript(ctor : Constructor, pref : String, node : Node) : Object {
347         var outp : Object = null;
348         try {
349             var cl : Class = getDefinitionByName(pref) as Class;
350             outp = new cl();
351             var values : Dictionary = Dictionary(ctor.constructMapping(node));
352             var props : Array = ObjectUtil.getClassInfo(outp).properties;
353             for(var key : Object in values) {
354                 var value : Object = values[key];
355                                 outp[key] = value;
356             } 
357         } catch(e : Error) {
358             throw new YAMLException("Can't construct actionscript object from class " + pref + ": " + e.toString());
359         }
360         return outp;
361     }
362
363     static: {
364         BaseConstructor.addConstructor("tag:yaml.org,2002:null", function call(self : Constructor, node : Node) : Object {
365                     return constructYamlNull(self,node);
366             });
367         addConstructor("tag:yaml.org,2002:bool", function call(self : Constructor, node : Node) : Object {
368                     return constructYamlBool(self,node);
369             });
370         addConstructor("tag:yaml.org,2002:omap", function call(self : Constructor, node : Node) : Object {
371                     return constructYamlOmap(self,node);
372             });
373         addConstructor("tag:yaml.org,2002:pairs", function call(self : Constructor, node : Node) : Object {
374                     return constructYamlPairs(self,node);
375             });
376         addConstructor("tag:yaml.org,2002:set", function call(self : Constructor, node : Node) : Object {
377                     return constructYamlSet(self,node);
378             });
379         addConstructor("tag:yaml.org,2002:int", function call(self : Constructor, node : Node) : Object {
380                     return constructYamlInt(self,node);
381             });
382         addConstructor("tag:yaml.org,2002:float", function call(self : Constructor, node : Node) : Object {
383                     return constructYamlFloat(self,node); 
384             });
385         addConstructor("tag:yaml.org,2002:timestamp", function call(self : Constructor, node : Node) : Object {
386                     return constructYamlTimestamp(self,node);
387             });
388         addConstructor("tag:yaml.org,2002:timestamp#ymd", function call(self : Constructor, node : Node) : Object {
389                     return constructYamlTimestamp(self,node);
390             });
391         addConstructor("tag:yaml.org,2002:str", function call(self : Constructor, node : Node) : Object {
392                     return constructYamlStr(self,node);
393             });
394         addConstructor("tag:yaml.org,2002:binary", function call(self : Constructor, node : Node) : Object {
395                     return constructYamlBinary(self,node);
396             });
397         addConstructor("tag:yaml.org,2002:seq", function call(self : Constructor, node : Node) : Object {
398                     return constructYamlSeq(self,node);
399             });
400         addConstructor("tag:yaml.org,2002:map", function call(self : Constructor, node : Node) : Object {
401                     return constructYamlMap(self,node);
402             });
403         addConstructor(null, function call(self : Constructor, node : Node) : Object {
404                     return self.constructPrivateType(node);
405             });
406
407         addMultiConstructor("tag:yaml.org,2002:seq:", function call(self : Constructor, pref : String, node : Node) : Object {
408                     return constructSpecializedSequence(self,pref,node);
409             });
410         addMultiConstructor("tag:yaml.org,2002:map:", function call(self : Constructor, pref : String, node : Node) : Object {
411                     return constructSpecializedMap(self,pref,node);
412             });
413         addMultiConstructor("!actionscript/object:", function call(self : Constructor, pref : String, node : Node) : Object {
414                     return constructActionscript(self,pref,node);
415            });
416  
417     }
418
419 }
420 }