1761c62186d7af08249660c938fbe33a763f0817
[potlatch2.git] / org / as3yaml / BaseConstructor.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.getQualifiedClassName;
26
27 import mx.logging.LogLogger;
28
29 import org.as3yaml.nodes.*;
30 import org.idmedia.as3commons.util.*;
31
32 public class BaseConstructor implements Constructor {
33     private static var yamlConstructors : Dictionary = new Dictionary();
34     private static var yamlMultiConstructors : Dictionary = new Dictionary();
35     private static var yamlMultiRegexps : Map = new HashMap();
36    
37     public function getYamlConstructor(key : Object) : Function {
38         return yamlConstructors[key] as Function;
39     }
40
41     public function getYamlMultiConstructor(key : Object) : Function {
42         return yamlMultiConstructors[key] as Function;
43     }
44
45     public function getYamlMultiRegexp(key : Object) : RegExp {
46         return yamlMultiRegexps[key] as RegExp;
47     }
48
49     public function getYamlMultiRegexps() : Map {
50         return yamlMultiRegexps;
51     }
52
53     public static function addConstructor(tag : String, ctor : Function) : void {
54         yamlConstructors[tag] = ctor;
55     }
56
57     public static function addMultiConstructor(tagPrefix : String, ctor : Function) : void {
58         yamlMultiConstructors[tagPrefix] = ctor;
59         yamlMultiRegexps.put(tagPrefix, new RegExp("^"+tagPrefix));
60     }
61
62     private var composer : Composer;
63     private var constructedObjects : Dictionary = new Dictionary();
64     private var recursiveObjects : Dictionary = new Dictionary();
65
66     public function BaseConstructor(composer : Composer) {
67         this.composer = composer;
68     }
69
70     public function checkData() : Boolean {
71         return composer.checkNode();
72     }
73
74     public function getData() : Object {
75         if(composer.checkNode()) {
76             var node : Node = composer.getNode();
77             if(null != node) {
78                 return constructDocument(node);
79             }
80         }
81         return null;
82     }
83
84     
85     public function eachDocument(ctor : Constructor) : Iterator {
86         return new DocumentIterator(ctor);
87     }
88
89     public function iterator() : Iterator {
90         return eachDocument(this);
91     }
92     
93     public function constructDocument(node : Node) : Object {
94         var data : Object = constructObject(node);
95         constructedObjects = new Dictionary();
96         recursiveObjects = new Dictionary();
97         return data;
98     }
99
100     public function constructObject(node : Node) : Object {
101         if(constructedObjects[node]) {
102             return constructedObjects[node];
103         }
104         if(recursiveObjects[node]) {
105             throw new ConstructorException(null,"found recursive node",null);
106         }
107         recursiveObjects[node] = null;
108         var data : Object;
109         var ctor : Function = getYamlConstructor(node.getTag());
110         if(ctor == null) {
111             var through : Boolean = true;
112             var yamlMultiRegExps : Array = getYamlMultiRegexps().keySet().toArray();
113             
114             for each(var tagPrefix : String in yamlMultiRegExps) {
115                 var reg : RegExp = getYamlMultiRegexp(tagPrefix);
116                 if(reg.exec(node.getTag())) {
117                     var tagSuffix : String = node.getTag().substring(tagPrefix.length);
118                     ctor = getYamlMultiConstructor(tagPrefix);
119                     data = ctor(this, tagSuffix, node);
120                     through = false;
121                     break;
122                 }
123             }
124             if(through) {
125                 var xctor : Function = getYamlMultiConstructor(null);
126                 if(null != xctor) {
127                     data = xctor(this, node.getTag(), node);
128                 } else {
129                     ctor = getYamlConstructor(null);
130                     if(ctor != null) {
131                         data = ctor(this,node)
132                     }
133                 }
134             }
135         } else {
136                 data = ctor(this,node);
137         }
138         constructedObjects[node] = data;
139         delete recursiveObjects[node];
140         return data;
141     }
142
143     public function constructPrimitive(node : Node) : Object {
144         if(node is ScalarNode) {
145             return constructScalar(node);
146         } else if(node is SequenceNode) {
147             return constructSequence(node);
148         } else if(node is MappingNode) {
149             return constructMapping(node);
150         } else {
151             new LogLogger('error').error(node.getTag());
152         }
153         return null;
154     }
155
156     public function constructScalar(node : Node) : Object {
157         if(!(node is ScalarNode)) {
158             if(node is MappingNode) {
159                 var vals : Dictionary = node.getValue() as Dictionary;
160                 for(var key : Object in vals) {
161                     if("tag:yaml.org,2002:value" == (key.getTag())) {
162                         return constructScalar(Node(vals.get(key)));
163                     }
164                 }
165             }
166             throw new ConstructorException(null,"expected a scalar node, but found " + getQualifiedClassName(node),null);
167         }
168         return node.getValue();
169     }
170
171     public function constructPrivateType(node : Node) : Object {
172         var val : Object = null;
173         if(node.getValue() is Map) {
174             val = constructMapping(node);
175         } else if(node.getValue() is List) {
176             val = constructSequence(node);
177         } else if (node.getValue() is Dictionary) {
178                 val = constructMapping(node);
179         } else {
180             val = node.getValue().toString();
181         }
182         return new PrivateType(node.getTag(),val);
183     } 
184     
185     public function constructSequence(node : Node) : Object {
186         if(!(node is SequenceNode)) {
187             throw new ConstructorException(null,"expected a sequence node, but found " + getQualifiedClassName(node),null);
188         }
189         var seq : Array = node.getValue() as Array;
190         var val : Array = new Array();
191         for each(var item:Node in seq) {
192             val.push(constructObject(item));
193         }
194         return val;
195     }
196
197     public function constructMapping(node : Node) : Object {
198         if(!(node is MappingNode)) {
199             throw new ConstructorException(null,"expected a mapping node, but found " + getQualifiedClassName(node),null);
200         }
201         var mapping : Dictionary = new Dictionary();
202         var merge : Array;
203         var val : Dictionary = node.getValue() as Dictionary;
204                 var key : Object;
205         for (key in val) {
206             var key_v : Node = key as Node;
207             var value_v : Node = val[key];
208             if(key_v.getTag() == ("tag:yaml.org,2002:merge")) {
209                 if(merge != null) {
210                     throw new ConstructorException("while constructing a mapping", "found duplicate merge key",null);
211                 }
212                 if(value_v is MappingNode) {
213                     merge = new Array();
214                     merge.push(constructMapping(value_v));
215                 } else if(value_v is SequenceNode) {
216                     merge = new Array();
217                     var vals : Array = value_v.getValue() as Array;
218                     for each(var subnode : Node in vals) {
219                         if(!(subnode is MappingNode)) {
220                             throw new ConstructorException("while constructing a mapping","expected a mapping for merging, but found " + getQualifiedClassName(subnode),null);
221                         }
222                         merge.unshift(constructMapping(subnode));
223                     }
224                 } else {
225                     throw new ConstructorException("while constructing a mapping","expected a mapping or list of mappings for merging, but found " + getQualifiedClassName(value_v),null);
226                 }
227             } else if(key_v.getTag() == ("tag:yaml.org,2002:value")) {
228                 if(mapping["="]) {
229                     throw new ConstructorException("while construction a mapping", "found duplicate value key", null);
230                 }
231                 mapping["="] = constructObject(value_v);
232             } else {
233                 var kObj : Object = constructObject(key_v), vObj : Object = constructObject(value_v);
234                 mapping[kObj] = vObj;
235             }
236         }
237         if(null != merge) {
238             merge.push(mapping);
239             mapping = new Dictionary();
240             for each(var item : Dictionary in merge) {
241                 for (key in item)
242                         mapping[key] = item[key];
243             }
244         }
245         return mapping;
246     }
247
248     public function constructPairs(node : Node) : Object {
249         if(!(node is MappingNode)) {
250             throw new ConstructorException(null,"expected a mapping node, but found " + getQualifiedClassName(node), null);
251         }
252         var value : Array = new Array();
253         var vals : Dictionary = node.getValue() as Dictionary;
254         for (var key : Object in vals) {
255             var val : Node = vals[key] as Node;
256             value.push([constructObject(key as Node),constructObject(val)]);
257         }
258         return value;
259     }
260     
261     
262     public function constructOmap (node : Node) : Object {
263         if(!(node is SequenceNode)) {
264             throw new ConstructorException(null,"expected a sequence node, but found " + getQualifiedClassName(node), null);
265         }
266         var value : Array = new Array();
267         var vals : Array = node.getValue() as Array;
268                 var addedKeyValHash : Object = new Object();
269                 
270         for each(var val : Node in vals) {  
271           
272             var hash : Dictionary = constructObject(val) as Dictionary;
273                         var hashSize: int = 0;
274             var hashKey: Object;
275             for (var key: Object in hash)
276             {  
277                 hashKey = key;
278                 hashSize++;
279             }
280             
281             if (hashSize > 1)
282                 throw new YAMLException("Each Map in an Ordered Map (!omap) is permitted to have only one key");
283           
284             var hashValue : Object = hash[hashKey];
285             
286             if(!(addedKeyValHash[hashKey] && (addedKeyValHash[hashKey] == hashValue)))
287             {
288                 value.push(hash);
289                 addedKeyValHash[hashKey] = hashValue;
290             }
291            
292         }
293         
294         return value;           
295     }
296     
297
298     public static function CONSTRUCT_PRIMITIVE(self : Constructor, node : Node) : Object {
299                 return self.constructPrimitive(node);
300             }
301     public static function CONSTRUCT_SCALAR(self : Constructor, node : Node) : Object {
302                 return self.constructScalar(node);
303             }
304     public static function CONSTRUCT_PRIVATE(self : Constructor, node : Node) : Object {
305                 return self.constructPrivateType(node);
306             }
307     public static function CONSTRUCT_SEQUENCE(self : Constructor, node : Node) : Object {
308                 return self.constructSequence(node);
309             }
310     public static function CONSTRUCT_MAPPING(self : Constructor, node : Node) : Object {
311                 return self.constructMapping(node);
312             }
313 }// BaseConstructorImpl
314 }
315         
316         import org.as3yaml.Constructor;
317         import org.as3yaml.nodes.Node
318
319         import org.idmedia.as3commons.util.Iterator;    
320
321 internal class DocumentIterator implements Iterator {
322         
323         private var _constructor : Constructor;
324         
325         public function DocumentIterator(ctor : Constructor) : void
326         {
327                 _constructor = ctor;
328         }
329     public function hasNext() : Boolean {return _constructor.checkData();}
330     public function next() : * {return _constructor.getData();}
331     public function remove() : void {}
332 }