1 /*******************************************************************************
\r
2 * Copyright (c) 2010 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.databoard.parser;
14 import java.io.IOException;
\r
16 import org.simantics.databoard.binding.ArrayBinding;
\r
17 import org.simantics.databoard.binding.Binding;
\r
18 import org.simantics.databoard.binding.BooleanBinding;
\r
19 import org.simantics.databoard.binding.ByteBinding;
\r
20 import org.simantics.databoard.binding.DoubleBinding;
\r
21 import org.simantics.databoard.binding.FloatBinding;
\r
22 import org.simantics.databoard.binding.IntegerBinding;
\r
23 import org.simantics.databoard.binding.LongBinding;
\r
24 import org.simantics.databoard.binding.MapBinding;
\r
25 import org.simantics.databoard.binding.OptionalBinding;
\r
26 import org.simantics.databoard.binding.RecordBinding;
\r
27 import org.simantics.databoard.binding.StringBinding;
\r
28 import org.simantics.databoard.binding.UnionBinding;
\r
29 import org.simantics.databoard.binding.VariantBinding;
\r
30 import org.simantics.databoard.binding.error.BindingException;
\r
31 import org.simantics.databoard.binding.error.RuntimeBindingException;
\r
32 import org.simantics.databoard.binding.mutable.MutableVariant;
\r
33 import org.simantics.databoard.file.RuntimeIOException;
\r
34 import org.simantics.databoard.parser.repository.DataTypeRepository;
\r
35 import org.simantics.databoard.parser.repository.DataValueRepository;
\r
36 import org.simantics.databoard.type.Component;
\r
37 import org.simantics.databoard.type.Datatype;
\r
38 import org.simantics.databoard.type.RecordType;
\r
39 import org.simantics.databoard.type.UnionType;
\r
42 * A class that converts values to their text presentation.
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.
48 * Names of referable record objects are acquired from a data values repository.
49 * If object is not in the repository, it is added.
51 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
53 public class DataValuePrinter implements Binding.Visitor1 {
57 PrintFormat format = PrintFormat.SINGLE_LINE;
59 DataValueRepository repo;
63 * Serialize value to a single line text string
69 * @throws BindingException
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);
81 * Write value to one or more lines
87 * @throws BindingException
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);
98 public DataValuePrinter(Appendable out, DataValueRepository valueRepository) {
100 this.repo = valueRepository;
103 public DataValueRepository getValueRepository() {
107 public DataTypeRepository getTypeRepository() {
108 return repo.getTypeRepository();
111 public void setOutput(Appendable out) {
115 public void setFormat(PrintFormat format) {
117 throw new IllegalArgumentException("null arg");
118 this.format = format;
121 public void print(MutableVariant variant) throws IOException,
123 print(variant.getBinding(), variant.getValue());
126 public void print(Binding binding, Object instance) throws IOException,
130 binding.accept(this, instance);
131 } catch (RuntimeIOException e) {
133 } catch (RuntimeBindingException e) {
141 public void visit(ArrayBinding b, Object instance)
142 throws RuntimeIOException, RuntimeBindingException {
144 Binding cb = b.getComponentBinding();
145 if (instance == null) {
146 out.append(format.openArray);
147 out.append(format.closeArray);
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);
\r
154 if (component == null)
\r
155 out.append("null");
\r
157 cb.accept(this, component);
160 out.append(format.arraySeparator);
\r
164 out.append(format.closeArray);
165 } catch (IOException e) {
166 throw new RuntimeIOException(e);
167 } catch (BindingException e) {
168 throw new RuntimeBindingException(e);
173 public void visit(BooleanBinding b, Object instance)
174 throws RuntimeIOException, RuntimeBindingException {
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);
186 public void visit(DoubleBinding b, Object instance)
187 throws RuntimeIOException, RuntimeBindingException {
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);
198 public void visit(FloatBinding b, Object instance)
199 throws RuntimeIOException, RuntimeBindingException {
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);
210 public void visit(IntegerBinding b, Object instance)
211 throws RuntimeIOException, RuntimeBindingException {
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);
222 public void visit(ByteBinding b, Object instance)
223 throws RuntimeIOException, RuntimeBindingException {
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);
234 public void visit(LongBinding b, Object instance)
235 throws RuntimeIOException, RuntimeBindingException {
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);
246 public void visit(OptionalBinding b, Object instance)
247 throws RuntimeIOException, RuntimeBindingException {
248 if (!b.hasValueUnchecked(instance))
250 out.append(format.Null);
252 } catch (IOException e) {
253 throw new RuntimeIOException(e);
257 instance = b.getValue(instance);
258 b.getComponentBinding().accept(this, instance);
259 } catch (BindingException e) {
260 throw new RuntimeBindingException(e);
265 * Create a new unique name that doesn't exist in the value repository.
269 String createNewName() {
272 name = "obj" + (nameCounter++);
273 } while (repo.get(name)!=null);
278 public void visit(RecordBinding b, Object instance)
279 throws RuntimeIOException, RuntimeBindingException {
280 boolean singleLine = format.newLine == null;
281 boolean tuple = b.type().isTupleType();
283 RecordType type = b.type();
284 Binding[] bindings = b.getComponentBindings();
285 Component[] components = b.type().getComponents();
286 int len = bindings.length;
289 if (type.isReferable() && instance!=root) {
290 String name = repo.getName(instance);
292 name = createNewName();
293 repo.put(name, b, instance);
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);
307 out.append(format.arraySeparator);
311 String name = repo.getName(value);
318 componentBinding.accept(this, value);
321 out.append(format.closeTuple);
323 } else if (len == 0) {
324 out.append(format.openRecord);
325 out.append(format.closeRecord);
326 } else if (singleLine) {
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);
333 // Omit null OptionalType (Syntactic Sugar)
334 if (componentBinding instanceof OptionalBinding) {
335 OptionalBinding ob = (OptionalBinding) componentBinding;
336 if (!ob.hasValue(value))
341 out.append(format.arraySeparator);
345 String fieldName = components[i].name;
346 putFieldName(fieldName);
350 String name = repo.getName(value);
355 componentBinding.accept(this, value);
358 out.append(format.closeRecord);
362 out.append(format.openRecord);
366 int fieldsLeft = 0;
\r
367 for (int i = 0; i < len; i++) {
\r
368 Binding componentBinding = bindings[i];
\r
369 Object value = b.getComponent(instance, i);
\r
370 if (componentBinding instanceof OptionalBinding) {
\r
371 OptionalBinding ob = (OptionalBinding) componentBinding;
\r
372 if (!ob.hasValue(value)) continue;
\r
377 for (int i = 0; i < len; i++) {
378 Binding componentBinding = bindings[i];
379 Object value = b.getComponent(instance, i);
381 // Omit null OptionalType (Syntactic Sugar)
382 if (componentBinding instanceof OptionalBinding) {
383 OptionalBinding ob = (OptionalBinding) componentBinding;
384 if (!ob.hasValue(value))
390 String fieldName = components[i].name;
391 putFieldName(fieldName);
395 String name = repo.getName(value);
400 componentBinding.accept(this, value);
403 // Add "," if there are more fields
406 out.append(format.arraySeparator);
\r
414 out.append(format.closeRecord);
417 } catch (IOException e) {
418 throw new RuntimeIOException(e);
419 } catch (BindingException e) {
420 throw new RuntimeBindingException(e);
425 public void visit(StringBinding b, Object instance)
426 throws RuntimeIOException, RuntimeBindingException {
428 boolean singleLineFormat = format.newLine == null;
429 String unescapedString = b.getValue(instance);
431 // Analyse the string
432 boolean canUseLongString = true;
433 boolean hasCharsToBeEscaped = false;
434 // boolean hasCharsToBeEscaped = true;
435 boolean hasLineFeeds = false;
440 for (int i = 0; i < unescapedString.length(); i++) {
443 c = unescapedString.charAt(i);
445 canUseLongString &= c != '\"' && pc != '\"' && ppc != '\"';
448 // Backspace \b or \u0008
450 hasCharsToBeEscaped = true;
454 hasCharsToBeEscaped = true;
456 // Backslash \\ or \u005c
458 hasCharsToBeEscaped = true;
460 // Double Quote \" or \u0022
462 hasCharsToBeEscaped = true;
464 // Single Quote \" or \u0027
466 hasCharsToBeEscaped = true;
468 // New Line \n or\u000a
470 hasCharsToBeEscaped = true;
473 // Carriage Return \r or\u000d
475 hasCharsToBeEscaped = true;
480 hasCharsToBeEscaped = true;
486 // Make a selection between short and long string
487 // Short string prints everything in a single line and can escape
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
492 if (canUseLongString && hasCharsToBeEscaped) {
493 if (singleLineFormat && hasLineFeeds)
494 putShortString(unescapedString);
496 putLongString(unescapedString);
498 putShortString(unescapedString);
501 } catch (BindingException e) {
502 throw new RuntimeBindingException(e);
503 } catch (IOException e) {
504 throw new RuntimeIOException(e);
509 public void visit(UnionBinding b, Object instance)
510 throws RuntimeIOException, RuntimeBindingException {
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();
519 String tagName = datatype.components[ordinal].name;
520 putFieldName(tagName);
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);
533 public void visit(MapBinding b, Object entity) {
534 boolean singleLine = format.newLine == null;
536 Binding keyBinding = b.getKeyBinding();
537 Binding valueBinding = b.getValueBinding();
538 int len = b.size(entity);
543 out.append(format.openRecord);
544 out.append(format.closeRecord);
546 else if (singleLine) {
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);
555 out.append(format.arraySeparator);
559 keyBinding.accept(this, key);
563 String name = repo.getName(value);
568 valueBinding.accept(this, value);
571 out.append(format.closeRecord);
575 out.append(format.openRecord);
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);
586 keyBinding.accept(this, key);
590 String name = repo.getName(value);
595 valueBinding.accept(this, value);
599 out.append(format.arraySeparator);
607 out.append(format.closeRecord);
610 } catch (IOException e) {
611 throw new RuntimeIOException(e);
612 } catch (BindingException e) {
613 throw new RuntimeBindingException(e);
618 public void visit(VariantBinding b, Object variant) {
620 Binding valueBinding = b.getContentBinding(variant);
621 Object value = b.getContent(variant, valueBinding);
622 Datatype type = b.getContentType(variant);
624 valueBinding.accept(this, value);
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());
633 } catch (IOException e) {
634 throw new RuntimeIOException(e);
635 } catch (BindingException e) {
636 throw new RuntimeBindingException(e);
640 private void putLongString(String unescapedString) throws IOException {
641 out.append(format.openLongString);
\r
642 // Escape the following characters \\ " \ \t
\r
643 for (int i = 0; i < unescapedString.length(); i++) {
\r
644 char c = unescapedString.charAt(i);
\r
646 // Backspace \b or \u0008
\r
654 // Backslash \\ or \u005c
\r
656 out.append("\\\\");
\r
658 // Single Quote \' or \u0027
\r
660 out.append("\\\'");
\r
662 // Double Quote \" or \u0022
\r
664 out.append("\\\"");
\r
675 out.append(format.closeLongString);
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);
684 // Backspace \b or \u0008
692 // Backslash \\ or \u005c
696 // Single Quote \' or \u0027
700 // Double Quote \" or \u0022
704 // New Line \n or\u000a
708 // Carriage Return \r or\u000d
721 out.append(format.closeString);
725 * Put a field name of a record type. The name is writted as a long string,
726 * if it contains " " or escapeable characters.
729 * @throws IOException
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);
737 hasCharsToBeEscaped = true;
740 hasCharsToBeEscaped = true;
743 hasCharsToBeEscaped = true;
746 hasCharsToBeEscaped = true;
749 hasCharsToBeEscaped = true;
752 hasCharsToBeEscaped = true;
755 hasCharsToBeEscaped = true;
758 hasCharsToBeEscaped = true;
764 if (hasCharsToBeEscaped) {
766 for (int i = 0; i < fieldName.length(); i++) {
767 char c = fieldName.charAt(i);
769 // Backspace \b or \u0008
777 // Backslash \\ or \u005c
781 // Double Quote \" or \u0022
785 // Single Quote \' or \u0027
789 // New Line \n or\u000a
793 // Carriage Return \r or\u000d
808 out.append(fieldName);
812 private void putIndent() throws IOException {
813 if (format.indent == null)
815 for (int j = 0; j < indentLevel; j++)
816 out.append(format.indent);
819 private void putLineFeed() throws IOException {
820 if (format.newLine == null)
822 out.append(format.newLine);
825 private void addIndent() {
826 if (format.indent == null)
831 private void decIndent() {
832 if (format.indent == null)