123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
|
/*******************************************************************************
copyright: Copyright (c) 2007 Kris Bell. All rights reserved
license: BSD style: $(LICENSE)
version: Initial release: Oct 2007
author: Kris
Simple serialization for text-based name/value pairs.
*******************************************************************************/
module tango.io.stream.Map;
private import tango.io.stream.Lines,
tango.io.stream.Buffered;
private import Text = tango.text.Util;
private import tango.io.device.Conduit;
/*******************************************************************************
Provides load facilities for a properties stream. That is, a file
or other medium containing lines of text with a name=value layout.
*******************************************************************************/
class MapInput(T) : Lines!(T)
{
/***********************************************************************
Propagate ctor to superclass.
***********************************************************************/
this (InputStream stream)
{
super (stream);
}
/***********************************************************************
Load properties from the provided stream, via a foreach.
We use an iterator to sweep text lines, and extract lValue
and rValue pairs from each one, The expected file format is
as follows:
* $(PRE
*x = y
*abc = 123
*x.y.z = this is a single property
*
*# this is a comment line)
Note that the provided name and value are actually slices
and should be copied if you intend to retain them (using
name.dup and value.dup where appropriate.)
***********************************************************************/
final int opApply (scope int delegate(ref const(T)[] name, ref const(T)[] value) dg)
{
int ret;
foreach (line; super)
{
auto text = Text.trim (line);
// comments require '#' as the first non-whitespace char
if (text.length && (text[0] != '#'))
{
// find the '=' char
auto i = Text.locate (text, cast(T) '=');
// ignore if not found ...
if (i < text.length)
{
auto name = Text.trim (text[0 .. i]);
auto value = Text.trim (text[i+1 .. $]);
if ((ret = dg (name, value)) != 0)
break;
}
}
}
return ret;
}
/***********************************************************************
Load the input stream into an AA.
***********************************************************************/
final MapInput load (ref const(T)[][T[]] properties)
{
foreach (name, value; this)
properties[name] = value;
return this;
}
}
/*******************************************************************************
Provides write facilities on a properties stream. That is, a file
or other medium which will contain lines of text with a name=value
layout.
*******************************************************************************/
class MapOutput(T) : OutputFilter
{
private const(T)[] eol;
private enum const(T)[] prefix = "# ";
private enum const(T)[] equals = " = ";
version (Win32)
private enum const(T)[] NL = "\r\n";
version (Posix)
private enum const(T)[] NL = "\n";
/***********************************************************************
Propagate ctor to superclass.
***********************************************************************/
this (OutputStream stream, const(T)[] newline = NL)
{
super (BufferedOutput.create (stream));
eol = newline;
}
/***********************************************************************
Append a newline to the provided stream.
***********************************************************************/
final MapOutput newline ()
{
sink.write (eol);
return this;
}
/***********************************************************************
Append a comment to the provided stream.
***********************************************************************/
final MapOutput comment (const(T)[] text)
{
sink.write (prefix);
sink.write (text);
sink.write (eol);
return this;
}
/***********************************************************************
Append name & value to the provided stream.
***********************************************************************/
final MapOutput append (const(T)[] name, const(T)[] value)
{
sink.write (name);
sink.write (equals);
sink.write (value);
sink.write (eol);
return this;
}
/***********************************************************************
Append AA properties to the provided stream.
***********************************************************************/
final MapOutput append (const(T)[][T[]] properties)
{
foreach (key, value; properties)
append (key, value);
return this;
}
}
/*******************************************************************************
*******************************************************************************/
debug (UnitTest)
{
import tango.io.Stdout;
import tango.io.device.Array;
unittest
{
auto buf = new Array(200);
auto input = new MapInput!(char)(buf);
auto output = new MapOutput!(char)(buf);
const(char)[][char[]] map;
map["foo"] = "bar";
map["foo2"] = "bar2";
output.append(map).flush();
map = map.init;
input.load (map);
assert (map["foo"] == "bar");
assert (map["foo2"] == "bar2");
}
}
|