split casing and fill onto separate levels
[potlatch2.git] / org / as3yaml / EmitterEnvironment.as
1 /*
2  * Copyright (c) 2007 Derek Wischusen
3  * 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy of 
5  * this software and associated documentation files (the "Software"), to deal in 
6  * the Software without restriction, including without limitation the rights to 
7  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
8  * of the Software, and to permit persons to whom the Software is furnished to do
9  * so, subject to the following conditions:
10  * 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  * 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
20  * SOFTWARE.
21  */
22
23 package org.as3yaml {
24         import org.as3yaml.events.*;
25         import org.idmedia.as3commons.util.ArrayList;
26         import org.idmedia.as3commons.util.HashMap;
27         import org.idmedia.as3commons.util.HashSet;
28         import org.idmedia.as3commons.util.Iterator;
29         import org.idmedia.as3commons.util.Map;
30         import org.idmedia.as3commons.util.Set;
31         
32
33     public class EmitterEnvironment {
34         public var states : ArrayList = new ArrayList();
35         public var state : int = Emitter.STREAM_START;
36         public var events : ArrayList = new ArrayList();
37         public var event : Event;
38         public var flowLevel : int = 0;
39         public var indents : ArrayList = new ArrayList();
40         public var indent : int = -1;
41         public var rootContext : Boolean = false;
42         public var sequenceContext : Boolean = false;
43         public var mappingContext : Boolean = false;
44         public var simpleKeyContext : Boolean = false;
45
46         public var line:int= 0;
47         public var column:int= 0;
48         public var whitespace:Boolean= true;
49         public var indentation:Boolean= true;
50         
51         public var canonical:Boolean= false;
52         public var bestIndent:int= 2;
53         public var bestWidth:int= 80;
54
55         public var bestLinebreak:String= "\n";
56
57         public var tagPrefixes:Map;
58
59         public var preparedAnchor:String;
60         public var preparedTag:String;
61         
62         public var analysis:ScalarAnalysis;
63         public var style:*= 0;
64
65         public var emitter:Emitter;
66
67         public var bestLineBreak:String= "\n";
68
69         public function needMoreEvents() : Boolean {
70             if(events.isEmpty()) {
71                 return true;
72             }
73             event = events.get(0) as Event;
74             if(event is DocumentStartEvent) {
75                 return needEvents(1);
76             } else if(event is SequenceStartEvent) {
77                 return needEvents(2);
78             } else if(event is MappingStartEvent) {
79                 return needEvents(3);
80             } else {
81                 return false;
82             }
83         }
84
85         private function needEvents(count : int) : Boolean {
86             var level : int = 0;
87             var iter : Iterator = events.iterator();
88             iter.next();
89             for(;iter.hasNext();) {
90                 var curr : Object = iter.next();
91                 if(curr is DocumentStartEvent || curr is CollectionStartEvent) {
92                     level++;
93                 } else if(curr is DocumentEndEvent || curr is CollectionEndEvent) {
94                     level--;
95                 } else if(curr is StreamEndEvent) {
96                     level = -1;
97                 }
98                 if(level<0) {
99                     return false;
100                 }
101             }
102             return events.size() < count+1;
103         }
104
105         private function increaseIndent(flow : Boolean, indentless : Boolean) : void {
106             indents.addAt(0,  new int(indent));
107             if(indent == -1) {
108                 if(flow) {
109                     indent = bestIndent;
110                 } else {
111                     indent = 0;
112                 }
113             } else if(!indentless) {
114                 indent += bestIndent;
115             }
116         }
117
118         public function expectStreamStart() : void {
119             if(this.event is StreamStartEvent) {
120                 emitter.writeStreamStart();
121                 this.state = Emitter.FIRST_DOCUMENT_START;
122             } else {
123                 throw new EmitterException("expected StreamStartEvent, but got " + this.event);
124             }
125         }
126         
127         public function expectNothing() : void {
128             throw new EmitterException("expecting nothing, but got " + this.event);
129         }
130
131         public function expectDocumentStart(first : Boolean) : void {
132             if(event is DocumentStartEvent) {
133                 var ev : DocumentStartEvent = DocumentStartEvent(event);
134                 if(first) {
135                     if(null != ev.getVersion()) {
136                         emitter.writeVersionDirective(emitter.prepareVersion(ev.getVersion()));
137                     }
138
139                     if((null != ev.getVersion() && ev.getVersion()[1] == 0) || emitter.getOptions().getVersion() == "1.0") {
140                         tagPrefixes = new HashMap();
141                         tagPrefixes.putAll(Emitter.DEFAULT_TAG_PREFIXES_1_0);
142                     } else {
143                         tagPrefixes = new HashMap();
144                         tagPrefixes.putAll(Emitter.DEFAULT_TAG_PREFIXES_1_1);
145                     }
146
147                     if(null != ev.getTags()) {
148                         var handles : Set = new HashSet();
149                         handles.addAll(ev.getTags().keySet());
150                         for(var iter : Iterator = handles.iterator();iter.hasNext();) {
151                             var handle : String = iter.next() as String;
152                             var prefix : String = ev.getTags().get(handle) as String;
153                             tagPrefixes.put(prefix,handle);
154                             var handleText : String = Emitter.prepareTagHandle(handle);
155                             var prefixText : String = Emitter.prepareTagPrefix(prefix);
156                             emitter.writeTagDirective(handleText,prefixText);
157                         }
158                     }
159                 }
160
161                 var implicit : Boolean = first && !ev.getExplicit() && !canonical && ev.getVersion() == null && ev.getTags() == null && !checkEmptyDocument();
162                 if(!implicit) {
163                     emitter.writeIndent();
164                     emitter.writeIndicator("--- ",true,true,false);
165                     if(canonical) {
166                         emitter.writeIndent();
167                     }
168                 }
169                 state = Emitter.DOCUMENT_ROOT;
170             } else if(event is StreamEndEvent) {
171                 emitter.writeStreamEnd();
172                 state = Emitter.NOTHING;
173             } else {
174                 throw new EmitterException("expected DocumentStartEvent, but got " + event);
175             }
176         }
177
178         public function expectDocumentRoot() : void {
179             states.addAt(0,new int(Emitter.DOCUMENT_END));
180             expectNode(true,false,false,false);
181         }
182
183         public function expectDocumentEnd() : void {
184             if(event is DocumentEndEvent) {
185                 emitter.writeIndent();
186                 if((DocumentEndEvent(event)).getExplicit()) {
187                     emitter.writeIndicator("...",true,false,false);
188                     emitter.writeIndent();
189                 }
190                 emitter.flushStream();
191                 state = Emitter.DOCUMENT_START;
192             } else {
193                 throw new EmitterException("expected DocumentEndEvent, but got " + event);
194             }
195         }
196
197         public function expectFirstFlowSequenceItem() : void {
198             if(event is SequenceEndEvent) {
199                 indent = int(indents.removeAtAndReturn(0));
200                 flowLevel--;
201                 emitter.writeIndicator("]",false,false,false);
202                 state = int(states.removeAtAndReturn(0));
203             } else {
204                 if(canonical || column > bestWidth) {
205                     emitter.writeIndent();
206                 }
207                 states.addAt(0,new int(Emitter.FLOW_SEQUENCE_ITEM));
208                 expectNode(false,true,false,false);
209             }
210         }
211
212         public function expectFlowSequenceItem() : void {
213             if(event is SequenceEndEvent) {
214                 indent = int(indents.removeAtAndReturn(0));
215                 flowLevel--;
216                 if(canonical) {
217                     emitter.writeIndicator(",",false,false,false);
218                     emitter.writeIndent();
219                 }
220                 emitter.writeIndicator("]",false,false,false);
221                 state = int(states.removeAtAndReturn(0));
222             } else {
223                 emitter.writeIndicator(",",false,false,false);
224                 if(canonical || column > bestWidth) {
225                     emitter.writeIndent();
226                 }
227                 states.addAt(0,new int(Emitter.FLOW_SEQUENCE_ITEM));
228                 expectNode(false,true,false,false);
229             }
230         }
231
232         public function expectFirstFlowMappingKey() : void {
233             if(event is MappingEndEvent) {
234                 indent = int(indents.removeAtAndReturn(0));
235                 flowLevel--;
236                 emitter.writeIndicator("}",false,false,false);
237                 state = int(states.removeAtAndReturn(0));
238             } else {
239                 if(canonical || column > bestWidth) {
240                     emitter.writeIndent();
241                 }
242                 if(!canonical && checkSimpleKey()) {
243                     states.addAt(0,new int(Emitter.FLOW_MAPPING_SIMPLE_VALUE));
244                     expectNode(false,false,true,true);
245                 } else {
246                     emitter.writeIndicator("?",true,false,false);
247                     states.addAt(0,new int(Emitter.FLOW_MAPPING_VALUE));
248                     expectNode(false,false,true,false);
249                 }
250             }
251         }
252
253         public function expectFlowMappingSimpleValue() : void {
254             emitter.writeIndicator(": ",false,true,false);
255             states.addAt(0,new int(Emitter.FLOW_MAPPING_KEY));
256             expectNode(false,false,true,false);
257         }
258
259         public function expectFlowMappingValue() : void {
260             if(canonical || column > bestWidth) {
261                 emitter.writeIndent();
262             }
263             emitter.writeIndicator(": ",false,true,false);
264             states.addAt(0,new int(Emitter.FLOW_MAPPING_KEY));
265             expectNode(false,false,true,false);
266         }
267
268         public function expectFlowMappingKey() : void {
269             if(event is MappingEndEvent) {
270                 indent = (int(indents.removeAtAndReturn(0)));
271                 flowLevel--;
272                 if(canonical) {
273                     emitter.writeIndicator(",",false,false,false);
274                     emitter.writeIndent();
275                 }
276                 emitter.writeIndicator("}",false,false,false);
277                 state = int(states.removeAtAndReturn(0));
278             } else {
279                 emitter.writeIndicator(",",false,false,false);
280                 if(canonical || column > bestWidth) {
281                     emitter.writeIndent();
282                 }
283                 if(!canonical && checkSimpleKey()) {
284                     states.addAt(0,new int(Emitter.FLOW_MAPPING_SIMPLE_VALUE));
285                     expectNode(false,false,true,true);
286                 } else {
287                     emitter.writeIndicator("?",true,false,false);
288                     states.addAt(0,new int(Emitter.FLOW_MAPPING_VALUE));
289                     expectNode(false,false,true,false);
290                 }
291             }
292         }
293
294         public function expectBlockSequenceItem(first : Boolean) : void  {
295             if(!first && event is SequenceEndEvent) {
296                 indent = int(indents.removeAtAndReturn(0));
297                 state = int(states.removeAtAndReturn(0));
298             } else {
299                 emitter.writeIndent();
300                 emitter.writeIndicator("-",true,false,true);
301                 states.addAt(0,new int(Emitter.BLOCK_SEQUENCE_ITEM));
302                 expectNode(false,true,false,false);
303             }
304         }
305
306         public function expectFirstBlockMappingKey() : void {
307             expectBlockMappingKey(true);
308         }
309
310         public function expectBlockMappingSimpleValue() : void {
311             emitter.writeIndicator(": ",false,true,false);
312             states.addAt(0,new int(Emitter.BLOCK_MAPPING_KEY));
313             expectNode(false,false,true,false);
314         }
315
316         public function expectBlockMappingValue() : void {
317             emitter.writeIndent();
318             emitter.writeIndicator(": ",true,true,true);
319             states.addAt(0,new int(Emitter.BLOCK_MAPPING_KEY));
320             expectNode(false,false,true,false);
321         }
322
323         public function expectBlockMappingKey(first : Boolean) : void {
324             if(!first && event is MappingEndEvent) {
325                 indent = int(indents.removeAtAndReturn(0));
326                 state = int(states.removeAtAndReturn(0));
327             } else {
328                 emitter.writeIndent();
329                 if(checkSimpleKey()) {
330                     states.addAt(0,new int(Emitter.BLOCK_MAPPING_SIMPLE_VALUE));
331                     expectNode(false,false,true,true);
332                 } else {
333                     emitter.writeIndicator("?",true,false,true);
334                     states.addAt(0,new int(Emitter.BLOCK_MAPPING_VALUE));
335                     expectNode(false,false,true,false);
336                 }
337             }
338         }
339
340         private function expectNode(root : Boolean, sequence : Boolean, mapping : Boolean, simpleKey : Boolean) : void {
341             rootContext = root;
342             sequenceContext = sequence;
343             mappingContext = mapping;
344             simpleKeyContext = simpleKey;
345             if(event is AliasEvent) {
346                 expectAlias();
347             } else if(event is ScalarEvent || event is CollectionStartEvent) {
348                 processAnchor("&");
349                 processTag();
350                 if(event is ScalarEvent) {
351                     expectScalar();
352                 } else if(event is SequenceStartEvent) {
353                     if(flowLevel != 0 || canonical || ((SequenceStartEvent(event))).getFlowStyle() || checkEmptySequence()) {
354                         expectFlowSequence();
355                     } else {
356                         expectBlockSequence();
357                     }
358                 } else if(event is MappingStartEvent) {
359                     if(flowLevel != 0 || canonical || ((MappingStartEvent(event))).getFlowStyle() || checkEmptyMapping()) {
360                         expectFlowMapping();
361                     } else {
362                         expectBlockMapping();
363                     }
364                 }
365             } else {
366                 throw new EmitterException("expected NodeEvent, but got " + event);
367             }
368         }
369         
370         private function expectAlias() : void {
371             if((NodeEvent(event)).getAnchor() == null) {
372                 throw new EmitterException("anchor is not specified for alias");
373             }
374             processAnchor("*");
375             state = int(states.removeAtAndReturn(0));
376         }
377
378         private function expectScalar() : void {
379             increaseIndent(true,false);
380             processScalar();
381             indent = int(indents.removeAtAndReturn(0));
382             state = int(states.removeAtAndReturn(0));
383         }
384
385         private function expectFlowSequence() : void {
386             emitter.writeIndicator("[",true,true,false);
387             flowLevel++;
388             increaseIndent(true,false);
389             state = Emitter.FIRST_FLOW_SEQUENCE_ITEM;
390         }
391
392         private function expectBlockSequence() : void {
393             increaseIndent(false, !mappingContext && !indentation);
394             state = Emitter.FIRST_BLOCK_SEQUENCE_ITEM;
395         }
396
397         private function expectFlowMapping(): void {
398             emitter.writeIndicator("{",true,true,false);
399             flowLevel++;
400             increaseIndent(true,false);
401             state = Emitter.FIRST_FLOW_MAPPING_KEY;
402         }
403
404         private function expectBlockMapping() : void { 
405             increaseIndent(false,false);
406             state = Emitter.FIRST_BLOCK_MAPPING_KEY;
407         }
408
409         private function checkEmptySequence() : Boolean {
410             return event is SequenceStartEvent && !events.isEmpty() && events.get(0) is SequenceEndEvent;
411         }
412
413         private function checkEmptyMapping() : Boolean {
414             return event is MappingStartEvent && !events.isEmpty() && events.get(0) is MappingEndEvent;
415         }
416
417         private function checkEmptyDocument() : Boolean {
418             if(!(event is DocumentStartEvent) || events.isEmpty()) {
419                 return false;
420             }
421             var ev : Event = Event(events.get(0));
422             return ev is ScalarEvent && (ScalarEvent(ev)).getAnchor() == null && (ScalarEvent(ev)).getTag() == null && (ScalarEvent(ev)).getImplicit() != null && (ScalarEvent(ev)).getValue() == "";
423         }
424
425         private function checkSimpleKey() : Boolean {
426             var length : int = 0;
427             if(event is NodeEvent && null != (NodeEvent(event)).getAnchor()) {
428                 if(null == preparedAnchor) {
429                     preparedAnchor = Emitter.prepareAnchor((NodeEvent(event)).getAnchor());
430                 }
431                 length += preparedAnchor.length;
432             }
433             var tag : String = null;
434             if(event is ScalarEvent) {
435                 tag = (ScalarEvent(event)).getTag();
436             } else if(event is CollectionStartEvent) {
437                 tag = (CollectionStartEvent(event)).getTag();
438             }
439             if(tag != null) {
440                 if(null == preparedTag) {
441                     preparedTag = emitter.prepareTag(tag);
442                 }
443                 length += preparedTag.length;
444             }
445             if(event is ScalarEvent) {
446                 if(null == analysis) {
447                     analysis = Emitter.analyzeScalar((ScalarEvent(event)).getValue());
448                     length += analysis.scalar.length;
449                 }
450             }
451
452             return (length < 128 && (event is AliasEvent || (event is ScalarEvent && !analysis.empty && !analysis.multiline) || checkEmptySequence() || checkEmptyMapping()));
453         }
454         
455         private function processAnchor(indicator : String) : void {
456             var ev : NodeEvent = event as NodeEvent;
457             if(null == ev.getAnchor()) {
458                 preparedAnchor = null;
459                 return;
460             }
461             if(null == preparedAnchor) {
462                 preparedAnchor = Emitter.prepareAnchor(ev.getAnchor());
463             }
464             if(preparedAnchor != null && "" != preparedAnchor) {
465                 emitter.writeIndicator(indicator+preparedAnchor,true,false,false);
466             }
467             preparedAnchor = null;
468         }
469         
470         private function processTag() : void {
471             var tag : String = null;
472             if(event is ScalarEvent) {
473                 var ev : ScalarEvent = ScalarEvent(event);
474                 tag = ev.getTag();
475                 if(style == 0) {
476                     style = chooseScalarStyle();
477                 }
478                 if(((!canonical || tag == null) && (('0' == style && ev.getImplicit()[0]) || ('0' != style && ev.getImplicit()[1])))) {
479                     preparedTag = null;
480                     return;
481                 }
482                 if(ev.getImplicit()[0] && null == tag) {
483                     tag = "!";
484                     preparedTag = null;
485                 }
486             } else {
487                 var eve : CollectionStartEvent = event as CollectionStartEvent;
488                 tag = eve.getTag();
489                 if((!canonical || tag == null) && eve.getImplicit()) {
490                     preparedTag = null;
491                     return;
492                 }
493             }
494             if(tag == null) {
495                 throw new EmitterException("tag is not specified");
496             }
497             if(null == preparedTag) {
498                 preparedTag = emitter.prepareTag(tag);
499             }
500             if(preparedTag != null && "" != preparedTag) {
501                 emitter.writeIndicator(preparedTag,true,false,false);
502             }
503             preparedTag = null;
504         }
505
506         private function chooseScalarStyle() : String {
507              var ev : ScalarEvent = ScalarEvent(event);
508
509             if(null == analysis) {
510                 analysis = Emitter.analyzeScalar(ev.getValue());
511             }
512
513             if(ev.getStyle() == '"' || this.canonical) {
514                 return '"';
515             }
516             
517             if(ev.getStyle() == '0') {
518                 if(!(simpleKeyContext && (analysis.empty || analysis.multiline)) && ((flowLevel != 0 && analysis.allowFlowPlain) || (flowLevel == 0 && analysis.allowBlockPlain))) {
519                     return '0';
520                 }
521             }
522             if(ev.getStyle() == '0' && ev.getImplicit()[0] && (!(simpleKeyContext && (analysis.empty || analysis.multiline)) && (flowLevel!=0 && analysis.allowFlowPlain || (flowLevel == 0 && analysis.allowBlockPlain)))) {
523                 return '0';
524             }
525             if((ev.getStyle() == '|') && flowLevel == 0 && analysis.allowBlock) {
526                 return '|';
527             }            
528             if((ev.getStyle() == '>') && flowLevel == 0 && analysis.allowBlock) {
529                 return '\'';
530             }
531             if((ev.getStyle() == '0' || ev.getStyle() == '\'') && (analysis.allowSingleQuoted && !(simpleKeyContext && analysis.multiline))) {
532                 return '\'';
533             }
534
535                         var FIRST_SPACE : RegExp = new RegExp("(^|\n) ");
536             if(analysis.multiline && !FIRST_SPACE.exec(ev.getValue()).find() && !analysis.specialCharacters) {
537                 return '|';
538             }
539             return '"';
540             
541         }
542
543         private function processScalar() : void{
544             var ev : ScalarEvent = ScalarEvent(event);
545
546             if(null == analysis) {
547                 analysis = Emitter.analyzeScalar(ev.getValue());
548             }
549             if(0 == style) {
550                 style = chooseScalarStyle();
551             }
552             var split : Boolean = !simpleKeyContext;
553             if(style == '"') {
554                 emitter.writeDoubleQuoted(analysis.scalar,split);
555             } else if(style == '\'') {
556                 emitter.writeSingleQuoted(analysis.scalar,split);
557             } else if(style == '>') {
558                 emitter.writeFolded(analysis.scalar);
559             } else if(style == '|') {
560                 emitter.writeLiteral(analysis.scalar);
561             } else {
562                 emitter.writePlain(analysis.scalar,split);
563             }
564             analysis = null;
565             style = 0;
566         }
567     }
568  }