]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/parser/DataValuePrinter.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / parser / DataValuePrinter.java
1 /*******************************************************************************
2  *  Copyright (c) 2010 Association for Decentralized Information Management in
3  *  Industry THTH ry.
4  *  All rights reserved. This program and the accompanying materials
5  *  are made available under the terms of the Eclipse Public License v1.0
6  *  which accompanies this distribution, and is available at
7  *  http://www.eclipse.org/legal/epl-v10.html
8  *
9  *  Contributors:
10  *      VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.parser;
13
14 import java.io.IOException;
15
16 import org.simantics.databoard.binding.ArrayBinding;
17 import org.simantics.databoard.binding.Binding;
18 import org.simantics.databoard.binding.BooleanBinding;
19 import org.simantics.databoard.binding.ByteBinding;
20 import org.simantics.databoard.binding.DoubleBinding;
21 import org.simantics.databoard.binding.FloatBinding;
22 import org.simantics.databoard.binding.IntegerBinding;
23 import org.simantics.databoard.binding.LongBinding;
24 import org.simantics.databoard.binding.MapBinding;
25 import org.simantics.databoard.binding.OptionalBinding;
26 import org.simantics.databoard.binding.RecordBinding;
27 import org.simantics.databoard.binding.StringBinding;
28 import org.simantics.databoard.binding.UnionBinding;
29 import org.simantics.databoard.binding.VariantBinding;
30 import org.simantics.databoard.binding.error.BindingException;
31 import org.simantics.databoard.binding.error.RuntimeBindingException;
32 import org.simantics.databoard.binding.mutable.MutableVariant;
33 import org.simantics.databoard.file.RuntimeIOException;
34 import org.simantics.databoard.parser.repository.DataTypeRepository;
35 import org.simantics.databoard.parser.repository.DataValueRepository;
36 import org.simantics.databoard.type.Component;
37 import org.simantics.databoard.type.Datatype;
38 import org.simantics.databoard.type.RecordType;
39 import org.simantics.databoard.type.UnionType;
40
41 /**
42  * A class that converts values to their text presentation.
43  * 
44  * Refereable records are printed after their name. The name is checked
45  * from a data value repository. If the record doesn't exist, a name is 
46  * made up and an entry is added. 
47  * 
48  * Names of referable record objects are acquired from a data values repository.
49  * If object is not in the repository, it is added. 
50  * 
51  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
52  */
53 public class DataValuePrinter implements Binding.Visitor1 {
54
55         Appendable out;
56         int indentLevel = 0;
57         PrintFormat format = PrintFormat.SINGLE_LINE;
58         int nameCounter = 1;
59         DataValueRepository repo;       
60         Object root;
61
62         /**
63          * Serialize value to a single line text string
64          * 
65          * @param type
66          * @param value
67          * @return the print
68          * @throws IOException
69          * @throws BindingException
70          */
71         public static String writeValueSingleLine(Binding type, Object value)
72                         throws IOException, BindingException {
73                 StringBuffer sb = new StringBuffer();
74                 DataValuePrinter writable = new DataValuePrinter(sb, new DataValueRepository());
75                 writable.setFormat(PrintFormat.SINGLE_LINE);
76                 writable.print(type, value);
77                 return sb.toString();
78         }
79
80         /**
81          * Write value to one or more lines
82          * 
83          * @param type
84          * @param value
85          * @return the print
86          * @throws IOException
87          * @throws BindingException
88          */
89         public static String writeValueMultiLine(Binding type, Object value)
90                         throws IOException, BindingException {
91                 StringBuffer sb = new StringBuffer();
92                 DataValuePrinter writable = new DataValuePrinter(sb, new DataValueRepository());
93                 writable.setFormat(PrintFormat.MULTI_LINE);
94                 writable.print(type, value);
95                 return sb.toString();
96         }
97
98         public DataValuePrinter(Appendable out, DataValueRepository valueRepository) {
99                 setOutput(out);
100                 this.repo = valueRepository;
101         }
102         
103         public DataValueRepository getValueRepository() {
104                 return repo;
105         }
106         
107         public DataTypeRepository getTypeRepository() {
108                 return repo.getTypeRepository();
109         }
110
111         public void setOutput(Appendable out) {
112                 this.out = out;
113         }
114
115         public void setFormat(PrintFormat format) {
116                 if (format == null)
117                         throw new IllegalArgumentException("null arg");
118                 this.format = format;
119         }
120
121         public void print(MutableVariant variant) throws IOException,
122         BindingException {
123                 print(variant.getBinding(), variant.getValue());
124         }
125         
126         public void print(Binding binding, Object instance) throws IOException,
127                         BindingException {
128                 try {
129                         root = instance;
130                         binding.accept(this, instance);
131                 } catch (RuntimeIOException e) {
132                         throw e.getCause();
133                 } catch (RuntimeBindingException e) {
134                         throw e.getCause();
135                 } finally {
136                         root = null;
137                 }
138         }
139
140         @Override
141         public void visit(ArrayBinding b, Object instance)
142                         throws RuntimeIOException, RuntimeBindingException {
143                 try {
144                         Binding cb = b.getComponentBinding();
145                         if (instance == null) {
146                                 out.append(format.openArray);
147                                 out.append(format.closeArray);
148                                 return;
149                         }
150                         int len = b.size(instance);
151                         out.append(format.openArray);
152                         for (int i = 0; i < len; i++) {
153                                 Object component = b.get(instance, i);
154                                 if (component == null)
155                                         out.append("null");
156                                 else
157                                         cb.accept(this, component);
158                                 
159                                 if (i < len - 1) {
160                                         out.append(format.arraySeparator);
161                                         out.append(' ');
162                                 }
163                         }
164                         out.append(format.closeArray);
165                 } catch (IOException e) {
166                         throw new RuntimeIOException(e);
167                 } catch (BindingException e) {
168                         throw new RuntimeBindingException(e);
169                 }
170         }
171
172         @Override
173         public void visit(BooleanBinding b, Object instance)
174                         throws RuntimeIOException, RuntimeBindingException {
175                 try {
176                         boolean value = b.getValue_(instance);
177                         out.append(value ? format.True : format.False);
178                 } catch (BindingException e) {
179                         throw new RuntimeBindingException(e);
180                 } catch (IOException e) {
181                         throw new RuntimeIOException(e);
182                 }
183         }
184
185         @Override
186         public void visit(DoubleBinding b, Object instance)
187                         throws RuntimeIOException, RuntimeBindingException {
188                 try {
189                         out.append(b.getValue(instance).toString());
190                 } catch (BindingException e) {
191                         throw new RuntimeBindingException(e);
192                 } catch (IOException e) {
193                         throw new RuntimeIOException(e);
194                 }
195         }
196
197         @Override
198         public void visit(FloatBinding b, Object instance)
199                         throws RuntimeIOException, RuntimeBindingException {
200                 try {
201                         out.append(b.getValue(instance).toString());
202                 } catch (BindingException e) {
203                         throw new RuntimeBindingException(e);
204                 } catch (IOException e) {
205                         throw new RuntimeIOException(e);
206                 }
207         }
208
209         @Override
210         public void visit(IntegerBinding b, Object instance)
211                         throws RuntimeIOException, RuntimeBindingException {
212                 try {
213                         out.append(b.getValue(instance).toString());
214                 } catch (BindingException e) {
215                         throw new RuntimeBindingException(e);
216                 } catch (IOException e) {
217                         throw new RuntimeIOException(e);
218                 }
219         }
220
221         @Override
222         public void visit(ByteBinding b, Object instance)
223                         throws RuntimeIOException, RuntimeBindingException {
224                 try {
225                         out.append(b.getValue(instance).toString());
226                 } catch (IOException e) {
227                         throw new RuntimeIOException(e);
228                 } catch (BindingException e) {
229                         throw new RuntimeBindingException(e);
230                 }
231         }
232
233         @Override
234         public void visit(LongBinding b, Object instance)
235                         throws RuntimeIOException, RuntimeBindingException {
236                 try {
237                         out.append(b.getValue(instance).toString());
238                 } catch (BindingException e) {
239                         throw new RuntimeBindingException(e);
240                 } catch (IOException e) {
241                         throw new RuntimeIOException(e);
242                 }
243         }
244
245         @Override
246         public void visit(OptionalBinding b, Object instance)
247                         throws RuntimeIOException, RuntimeBindingException {
248                 if (!b.hasValueUnchecked(instance))
249                         try {
250                                 out.append(format.Null);
251                                 return;
252                         } catch (IOException e) {
253                                 throw new RuntimeIOException(e);
254                         }
255
256                 try {
257                         instance = b.getValue(instance);
258                         b.getComponentBinding().accept(this, instance);
259                 } catch (BindingException e) {
260                         throw new RuntimeBindingException(e);
261                 }
262         }
263
264         /**
265          * Create a new unique name that doesn't exist in the value repository.
266          * 
267          * @return new name 
268          */
269         String createNewName() {
270                 String name;
271                 do {
272                         name = "obj" + (nameCounter++);
273                 } while (repo.get(name)!=null);
274                 return name;
275         }
276         
277         @Override
278         public void visit(RecordBinding b, Object instance)
279                         throws RuntimeIOException, RuntimeBindingException {
280                 boolean singleLine = format.newLine == null;
281                 boolean tuple = b.type().isTupleType();
282                 try {
283                         RecordType type = b.type();
284                         Binding[] bindings = b.getComponentBindings();
285                         Component[] components = b.type().getComponents();
286                         int len = bindings.length;
287
288                         // Has Name
289                         if (type.isReferable() && instance!=root) {
290                                 String name = repo.getName(instance);
291                                 if (name == null) {
292                                         name = createNewName();
293                                         repo.put(name, b, instance);
294                                 }
295                                 out.append(name);
296                                 return;
297                         }
298                         
299                         if (tuple) {
300
301                                 out.append(format.openTuple);
302                                 for (int i = 0; i < len; i++) {
303                                         Binding componentBinding = bindings[i];
304                                         Object value = b.getComponent(instance, i);
305
306                                         if (i > 0) {
307                                                 out.append(format.arraySeparator);
308                                         }
309
310                                         // Write object name
311                                         String name = repo.getName(value);
312                                         if (name != null) {
313                                                 out.append(name);
314                                                 continue;
315                                         }
316
317                                         // Write value
318                                         componentBinding.accept(this, value);
319
320                                 }
321                                 out.append(format.closeTuple);
322
323                         } else if (len == 0) {
324                                 out.append(format.openRecord);
325                                 out.append(format.closeRecord);
326                         } else if (singleLine) {
327
328                                 out.append(format.openRecord);
329                                 for (int i = 0; i < len; i++) {
330                                         Binding componentBinding = bindings[i];
331                                         Object value = b.getComponent(instance, i);
332
333                                         // Omit null OptionalType (Syntactic Sugar)
334                                         if (componentBinding instanceof OptionalBinding) {
335                                                 OptionalBinding ob = (OptionalBinding) componentBinding;
336                                                 if (!ob.hasValue(value))
337                                                         continue;
338                                         }
339
340                                         if (i > 0) {
341                                                 out.append(format.arraySeparator);
342                                                 out.append(' ');
343                                         }
344
345                                         String fieldName = components[i].name;
346                                         putFieldName(fieldName);
347                                         out.append(" = ");
348
349                                         // Write object name
350                                         String name = repo.getName(value);
351                                         if (name != null) {
352                                                 out.append(name);
353                                         } else {
354                                                 // Write value
355                                                 componentBinding.accept(this, value);
356                                         }
357                                 }
358                                 out.append(format.closeRecord);
359
360                         } else {
361
362                                 out.append(format.openRecord);
363                                 putLineFeed();
364                                 addIndent();
365                                 try {
366                                         int fieldsLeft = 0;
367                                         for (int i = 0; i < len; i++) {
368                                                 Binding componentBinding = bindings[i];
369                                                 Object value = b.getComponent(instance, i);
370                                                 if (componentBinding instanceof OptionalBinding) {
371                                                         OptionalBinding ob = (OptionalBinding) componentBinding;
372                                                         if (!ob.hasValue(value)) continue;
373                                                 }
374                                                 fieldsLeft++;
375                                         }
376                                         
377                                         for (int i = 0; i < len; i++) {
378                                                 Binding componentBinding = bindings[i];
379                                                 Object value = b.getComponent(instance, i);
380
381                                                 // Omit null OptionalType (Syntactic Sugar)
382                                                 if (componentBinding instanceof OptionalBinding) {
383                                                         OptionalBinding ob = (OptionalBinding) componentBinding;
384                                                         if (!ob.hasValue(value))
385                                                                 continue;
386                                                 }
387
388                                                 putIndent();
389
390                                                 String fieldName = components[i].name;
391                                                 putFieldName(fieldName);
392                                                 out.append(" = ");
393
394                                                 // Write object name
395                                                 String name = repo.getName(value);
396                                                 if (name != null) {
397                                                         out.append(name);
398                                                 } else {
399                                                         // Write value
400                                                         componentBinding.accept(this, value);
401                                                 }
402
403                                                 // Add "," if there are more fields
404                                                 fieldsLeft--;
405                                                 if (fieldsLeft>0)
406                                                         out.append(format.arraySeparator);
407                                                 putLineFeed();
408                                         }
409
410                                 } finally {
411                                         decIndent();
412                                 }
413                                 putIndent();
414                                 out.append(format.closeRecord);
415                         }
416
417                 } catch (IOException e) {
418                         throw new RuntimeIOException(e);
419                 } catch (BindingException e) {
420                         throw new RuntimeBindingException(e);
421                 }
422         }
423
424         @Override
425         public void visit(StringBinding b, Object instance)
426                         throws RuntimeIOException, RuntimeBindingException {
427                 try {
428                         boolean singleLineFormat = format.newLine == null;
429                         String unescapedString = b.getValue(instance);
430
431                         // Analyse the string
432                         boolean canUseLongString = true;
433                         boolean hasCharsToBeEscaped = false;
434 //                      boolean hasCharsToBeEscaped = true;
435                         boolean hasLineFeeds = false;
436
437                         char c = 0x00;
438                         char pc = 0x00;
439                         char ppc = 0x00;
440                         for (int i = 0; i < unescapedString.length(); i++) {
441                                 ppc = pc;
442                                 pc = c;
443                                 c = unescapedString.charAt(i);
444
445                                 canUseLongString &= c != '\"' && pc != '\"' && ppc != '\"';
446
447                                 switch (c) {
448                                 // Backspace \b or \u0008
449                                 case '\b': {
450                                         hasCharsToBeEscaped = true;
451                                 }
452                                         // Form feed \f or
453                                 case '\f': {
454                                         hasCharsToBeEscaped = true;
455                                 }
456                                         // Backslash \\ or \u005c
457                                 case '\\': {
458                                         hasCharsToBeEscaped = true;
459                                 }
460                                         // Double Quote \" or \u0022
461                                 case '\"': {
462                                         hasCharsToBeEscaped = true;
463                                 }
464                                         // Single Quote \" or \u0027
465                                 case '\'': {
466                                         hasCharsToBeEscaped = true;
467                                 }
468                                         // New Line \n or\u000a
469                                 case '\n': {
470                                         hasCharsToBeEscaped = true;
471                                         hasLineFeeds = true;
472                                 }
473                                         // Carriage Return \r or\u000d
474                                 case '\r': {
475                                         hasCharsToBeEscaped = true;
476                                         hasLineFeeds = true;
477                                 }
478                                         // Tabulator \t or
479                                 case '\t': {
480                                         hasCharsToBeEscaped = true;
481                                 }
482                                 default:
483                                 }
484                         }
485
486                         // Make a selection between short and long string
487                         // Short string prints everything in a single line and can escape
488                         // anything
489                         // Long string is more readable as it doesn't have escape characters
490                         // Prefer Long string over short if there are characters to escape
491
492                         if (canUseLongString && hasCharsToBeEscaped) {
493                                 if (singleLineFormat && hasLineFeeds)
494                                         putShortString(unescapedString);
495                                 else
496                                         putLongString(unescapedString);
497                         } else {
498                                 putShortString(unescapedString);
499                         }
500
501                 } catch (BindingException e) {
502                         throw new RuntimeBindingException(e);
503                 } catch (IOException e) {
504                         throw new RuntimeIOException(e);
505                 }
506         }
507
508         @Override
509         public void visit(UnionBinding b, Object instance)
510                         throws RuntimeIOException, RuntimeBindingException {
511                 try {
512                         UnionType datatype = (UnionType) b.type();
513                         int ordinal = b.getTag(instance);
514                         Object component = b.getValue(instance);
515                         Binding cb = b.getComponentBindings()[ordinal];
516                         // boolean isTuple = (cb instanceof RecordBinding) &&
517                         // ((RecordBinding)cb).getDataType().isTupleType();
518
519                         String tagName = datatype.components[ordinal].name;
520                         putFieldName(tagName);
521                         out.append(' ');
522                         // if (!isTuple) out.append( format.openUnion );
523                         cb.accept(this, component);
524                         // out.append( format.closeUnion );
525                 } catch (IOException e) {
526                         throw new RuntimeIOException(e);
527                 } catch (BindingException e) {
528                         throw new RuntimeBindingException(e);
529                 }
530         }
531
532         @Override
533     public void visit(MapBinding b, Object entity) {
534         boolean singleLine = format.newLine == null;  
535         try {
536                 Binding keyBinding = b.getKeyBinding();
537                 Binding valueBinding = b.getValueBinding();
538                 int len = b.size(entity);               
539                 
540                         out.append("map ");             
541                 
542             if (len==0) {
543                         out.append(format.openRecord);
544                         out.append(format.closeRecord);
545                 }
546                 else if (singleLine) {
547
548                         out.append(format.openRecord);
549                         Object keys[] = b.getKeys(entity);
550                         for (int i=0; i<len; i++) {
551                                 Object key = keys[i];
552                                 Object value = b.get(entity, key);
553                                 
554                                 if (i>0) {
555                                         out.append(format.arraySeparator);
556                                         out.append(' ');
557                                 }
558                                 
559                                 keyBinding.accept(this, key);
560                                 out.append( " = ");
561                                 
562                                 // Write object name
563                                 String name = repo.getName(value);
564                                 if (name != null) {
565                                         out.append(name);
566                                 } else {                                
567                                         // Write value
568                                         valueBinding.accept(this, value);
569                                 }
570                         }                       
571                         out.append(format.closeRecord);
572                         
573                 } else {
574
575                         out.append(format.openRecord);
576                                 putLineFeed();
577                         addIndent();
578                         try {
579                                 Object keys[] = b.getKeys(entity);
580                                 for (int i=0; i<len; i++) {
581                                         Object key = keys[i];
582                                         Object value = b.get(entity, key);
583         
584                                         putIndent();
585         
586                                         keyBinding.accept(this, key);
587                                         out.append( " = ");
588                                         
589                                         // Write object name
590                                         String name = repo.getName(value);
591                                         if (name != null) {
592                                                 out.append(name);
593                                         } else {                                
594                                                 // Write value
595                                                 valueBinding.accept(this, value);
596                                         }
597                                         
598                                         if (i<len-1)
599                                                 out.append(format.arraySeparator);
600                                         putLineFeed();
601                                 }                       
602
603                         } finally {
604                                 decIndent();
605                         }
606                         putIndent();
607                         out.append(format.closeRecord);
608                 }
609                 
610                 } catch (IOException e) {
611                         throw new RuntimeIOException(e);
612                 } catch (BindingException e) {
613                         throw new RuntimeBindingException(e);
614                 } 
615         }
616
617         @Override
618         public void visit(VariantBinding b, Object variant) {
619                 try {
620                         Binding valueBinding = b.getContentBinding(variant);
621                         Object value = b.getContent(variant, valueBinding);
622                         Datatype type = b.getContentType(variant);
623
624                         valueBinding.accept(this, value);
625
626                         out.append(" : ");
627
628                         //Binding typeBinding = Bindings.getBindingUnchecked(DataType.class);
629                         // TODO Use type name if available
630                         //typeBinding.printValue(type, out, true);
631                         out.append(type.toSingleLineString());
632
633                 } catch (IOException e) {
634                         throw new RuntimeIOException(e);
635                 } catch (BindingException e) {
636                         throw new RuntimeBindingException(e);
637                 }
638         }
639
640         private void putLongString(String unescapedString) throws IOException {
641                 out.append(format.openLongString);
642                 // Escape the following characters \\ " \ \t
643                 for (int i = 0; i < unescapedString.length(); i++) {
644                         char c = unescapedString.charAt(i);
645                         switch (c) {
646                         // Backspace \b or \u0008
647                         case '\b':
648                             out.append("\\b");
649                             break;
650                             // Form feed \f or
651                         case '\f':
652                             out.append("\\f");
653                             break;
654                             // Backslash \\ or \u005c
655                         case '\\':
656                             out.append("\\\\");
657                             break;
658                             // Single Quote \' or \u0027
659                         case '\'':
660                             out.append("\\\'");
661                             break;
662                             // Double Quote \" or \u0022
663                         case '\"':
664                             out.append("\\\"");
665                             break;
666                             // Tabulator \t or
667                         case '\t':
668                             out.append("\\t");
669                             break;
670                         default:
671                             out.append(c);
672                             break;
673                         }
674                 }
675                 out.append(format.closeLongString);
676         }
677
678         private void putShortString(String unescapedString) throws IOException {
679                 out.append(format.openString);
680                 // Escape the following characters \\ " \ \n \r \t
681                 for (int i = 0; i < unescapedString.length(); i++) {
682                         char c = unescapedString.charAt(i);
683                         switch (c) {
684                         // Backspace \b or \u0008
685                         case '\b':
686                                 out.append("\\b");
687                                 break;
688                         // Form feed \f or
689                         case '\f':
690                                 out.append("\\f");
691                                 break;
692                         // Backslash \\ or \u005c
693                         case '\\':
694                                 out.append("\\\\");
695                                 break;
696                         // Single Quote \' or \u0027
697                         case '\'':
698                                 out.append("\\\'");
699                                 break;
700                         // Double Quote \" or \u0022
701                         case '\"':
702                                 out.append("\\\"");
703                                 break;
704                         // New Line \n or\u000a
705                         case '\n':
706                                 out.append("\\n");
707                                 break;
708                         // Carriage Return \r or\u000d
709                         case '\r':
710                                 out.append("\\r");
711                                 break;
712                         // Tabulator \t or
713                         case '\t':
714                                 out.append("\\t");
715                                 break;
716                         default:
717                                 out.append(c);
718                                 break;
719                         }
720                 }
721                 out.append(format.closeString);
722         }
723
724         /**
725          * Put a field name of a record type. The name is writted as a long string,
726          * if it contains " " or escapeable characters.
727          * 
728          * @param fieldName
729          * @throws IOException
730          */
731         private void putFieldName(String fieldName) throws IOException {
732                 boolean hasCharsToBeEscaped = false;
733                 for (int i = 0; i < fieldName.length(); i++) {
734                         char c = fieldName.charAt(i);
735                         switch (c) {
736                         case '\b':
737                                 hasCharsToBeEscaped = true;
738                                 break;
739                         case '\f':
740                                 hasCharsToBeEscaped = true;
741                                 break;
742                         case '\\':
743                                 hasCharsToBeEscaped = true;
744                                 break;
745                         case '\"':
746                                 hasCharsToBeEscaped = true;
747                                 break;
748                         case '\'':
749                                 hasCharsToBeEscaped = true;
750                                 break;
751                         case '\n':
752                                 hasCharsToBeEscaped = true;
753                                 break;
754                         case '\r':
755                                 hasCharsToBeEscaped = true;
756                                 break;
757                         case '\t':
758                                 hasCharsToBeEscaped = true;
759                                 break;
760                         default:
761                         }
762                 }
763
764                 if (hasCharsToBeEscaped) {
765                         out.append('\'');
766                         for (int i = 0; i < fieldName.length(); i++) {
767                                 char c = fieldName.charAt(i);
768                                 switch (c) {
769                                 // Backspace \b or \u0008
770                                 case '\b':
771                                         out.append("\\b");
772                                         break;
773                                 // Form feed \f or
774                                 case '\f':
775                                         out.append("\\f");
776                                         break;
777                                 // Backslash \\ or \u005c
778                                 case '\\':
779                                         out.append("\\\\");
780                                         break;
781                                 // Double Quote \" or \u0022
782                                 case '\"':
783                                         out.append("\\\"");
784                                         break;
785                                 // Single Quote \' or \u0027
786                                 case '\'':
787                                         out.append("\\\'");
788                                         break;
789                                 // New Line \n or\u000a
790                                 case '\n':
791                                         out.append("\\n");
792                                         break;
793                                 // Carriage Return \r or\u000d
794                                 case '\r':
795                                         out.append("\\r");
796                                         break;
797                                 // Tabulator \t or
798                                 case '\t':
799                                         out.append("\\t");
800                                         break;
801                                 default:
802                                         out.append(c);
803                                         break;
804                                 }
805                         }
806                         out.append('\'');
807                 } else {
808                         out.append(fieldName);
809                 }
810         }
811
812         private void putIndent() throws IOException {
813                 if (format.indent == null)
814                         return;
815                 for (int j = 0; j < indentLevel; j++)
816                         out.append(format.indent);
817         }
818
819         private void putLineFeed() throws IOException {
820                 if (format.newLine == null)
821                         return;
822                 out.append(format.newLine);
823         }
824
825         private void addIndent() {
826                 if (format.indent == null)
827                         return;
828                 indentLevel++;
829         }
830
831         private void decIndent() {
832                 if (format.indent == null)
833                         return;
834                 indentLevel--;
835         }
836
837 }