//
// Copyright (c) 2009, SkyFoundry LLC
// All Rights Reserved
//
// History:
// 07 Oct 09 Brian Frank Creation
// 28 Dec 09 Brian Frank DataWriter => ZincWriter
//
**
** ZincWriter serializes Grids to an output stream.
** See [docSkyspark]`docSkySpark::Zinc`
**
@Js
class ZincWriter : GridWriter
{
Str zincVersion
//////////////////////////////////////////////////////////////////////////
// Convenience
//////////////////////////////////////////////////////////////////////////
**
** Format a grid to a zinc string in memory.
**
static Str gridToStr(Grid grid, Str version)
{
buf := StrBuf()
ZincWriter(buf.out, version).writeGrid(grid)
return buf.toStr
}
**
** Format a set of tags to a string in memory which can be parsed with
** `ZincReader.readTags`. The tags can be a 'Dict' or a 'Str:Obj' map.
**
static Str tagsToStr(Obj tags)
{
buf := StrBuf()
func := |Obj? val, Str name|
{
if (!buf.isEmpty) buf.addChar(' ')
buf.add(name)
try
if (val !== Marker.val) buf.addChar(':').add(scalarToStr(val))
catch (Err e)
throw IOErr("Cannot write tag $name; $e.msg")
}
if (tags is Dict) ((Dict)tags).each(func)
else ((Map)tags).each(func)
return buf.toStr
}
**
** Get a scalar value as a zinc string.
**
static Str scalarToStr(Obj? val)
{
// null
if (val == null) return "N"
// map to a Kind
t := val.typeof
kind := Kind.fromType(t, false) ?: throw IOErr("Not a valid scalar type: $t")
return kind.valToZinc(val)
}
//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////
** Wrap given output stream
new make(OutStream out, Str zincVersion) { this.out = out; this.zincVersion = zincVersion }
//////////////////////////////////////////////////////////////////////////
// Public
//////////////////////////////////////////////////////////////////////////
** Write a list of grids to stream
override This writeGrids(Grid[] grids)
{
grids.each |grid| { writeGrid(grid) }
return this
}
** Write a grid to stream
override This writeGrid(Grid grid)
{
// set meta-data line
out.print("ver:\"$zincVersion\"")
writeMeta(grid.meta)
out.writeChar('\n')
// columns lines
if (grid.cols.isEmpty)
{
// technicially this should be illegal, but
// for robustness handle it here
out.print("noCols\n")
}
else
{
grid.cols.each |col, i|
{
if (i > 0) out.writeChar(',')
writeCol(col)
}
out.writeChar('\n')
}
// rows
grid.each |row| { writeRow(row) }
out.writeChar('\n')
return this
}
** Write "\n" to stream
This nl() { out.writeChar('\n'); return this }
** Flush underlying stream
This flush() { out.flush; return this }
** Close underlying stream
This close() { out.close; return this }
//////////////////////////////////////////////////////////////////////////
// Helpers
//////////////////////////////////////////////////////////////////////////
private Void writeCol(Col col)
{
out.print(col.name)
writeMeta(col.meta)
}
private Void writeRow(Row row)
{
row.grid.cols.each |col, i|
{
if (i > 0) out.writeChar(',')
val := row.val(col)
try
{
if (val == null)
{
// if this is only column, then use explicit N for null
if (i == 0 && row.grid.cols.size == 1) out.writeChar('N')
}
else
{
writeScalar(val)
}
}
catch (Err e)
{
throw IOErr("Cannot write col '$col.name' = '$val'; $e.msg")
}
}
out.writeChar('\n')
}
private Void writeMeta(Dict m)
{
m.each |v, k|
{
out.print(" ").print(k)
try
if (v != Marker.val) { out.print(":"); writeScalar(v) }
catch (Err e)
throw IOErr("Cannot write meta $k: $v", e)
}
}
private Void writeScalar(Obj? val)
{
out.print(scalarToStr(val))
}
//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////
private OutStream out
}