132
|
1 /*******************************************************************************
|
|
2
|
|
3 copyright: Copyright (c) 2007 Kris Bell. All rights reserved
|
|
4
|
|
5 license: BSD style: $(LICENSE)
|
|
6
|
|
7 version: Initial release: Oct 2007
|
|
8
|
|
9 author: Kris
|
|
10
|
|
11 Simple serialization for text-based name/value pairs
|
|
12
|
|
13 *******************************************************************************/
|
|
14
|
|
15 module tango.io.stream.MapStream;
|
|
16
|
|
17 private import tango.io.Buffer,
|
|
18 tango.io.Conduit;
|
|
19
|
|
20 private import Text = tango.text.Util;
|
|
21
|
|
22 private import tango.text.stream.LineIterator;
|
|
23
|
|
24 /*******************************************************************************
|
|
25
|
|
26 Provides load facilities for a properties stream. That is, a file
|
|
27 or other medium containing lines of text with a name=value layout
|
|
28
|
|
29 *******************************************************************************/
|
|
30
|
|
31 class MapInput(T) : LineIterator!(T)
|
|
32 {
|
|
33 /***********************************************************************
|
|
34
|
|
35 Propagate ctor to superclass
|
|
36
|
|
37 ***********************************************************************/
|
|
38
|
|
39 this (InputStream stream)
|
|
40 {
|
|
41 super (stream);
|
|
42 }
|
|
43
|
|
44 /***********************************************************************
|
|
45
|
|
46 Load properties from the provided stream, via a foreach.
|
|
47
|
|
48 We use an iterator to sweep text lines, and extract lValue
|
|
49 and rValue pairs from each one, The expected file format is
|
|
50 as follows:
|
|
51
|
|
52 ---
|
|
53 x = y
|
|
54 abc = 123
|
|
55 x.y.z = this is a single property
|
|
56
|
|
57 # this is a comment line
|
|
58 ---
|
|
59
|
|
60 Note that the provided name and value are actually slices
|
|
61 and should be copied if you intend to retain them (using
|
|
62 name.dup and value.dup where appropriate)
|
|
63
|
|
64 ***********************************************************************/
|
|
65
|
|
66 final int opApply (int delegate(ref T[] name, ref T[] value) dg)
|
|
67 {
|
|
68 int ret;
|
|
69
|
|
70 foreach (line; super)
|
|
71 {
|
|
72 auto text = Text.trim (line);
|
|
73
|
|
74 // comments require '#' as the first non-whitespace char
|
|
75 if (text.length && (text[0] != '#'))
|
|
76 {
|
|
77 // find the '=' char
|
|
78 auto i = Text.locate (text, '=');
|
|
79
|
|
80 // ignore if not found ...
|
|
81 if (i < text.length)
|
|
82 {
|
|
83 auto name = Text.trim (text[0 .. i]);
|
|
84 auto value = Text.trim (text[i+1 .. $]);
|
|
85 if ((ret = dg (name, value)) != 0)
|
|
86 break;
|
|
87 }
|
|
88 }
|
|
89 }
|
|
90 return ret;
|
|
91 }
|
|
92
|
|
93 /***********************************************************************
|
|
94
|
|
95 Load the input stream into an AA
|
|
96
|
|
97 ***********************************************************************/
|
|
98
|
|
99 final MapInput load (ref T[][T[]] properties)
|
|
100 {
|
|
101 foreach (name, value; this)
|
|
102 properties[name.dup] = value.dup;
|
|
103 return this;
|
|
104 }
|
|
105 }
|
|
106
|
|
107
|
|
108 /*******************************************************************************
|
|
109
|
|
110 Provides write facilities on a properties stream. That is, a file
|
|
111 or other medium which will contain lines of text with a name=value
|
|
112 layout
|
|
113
|
|
114 *******************************************************************************/
|
|
115
|
|
116 class MapOutput(T) : OutputFilter, Buffered
|
|
117 {
|
|
118 private IBuffer output;
|
|
119
|
|
120 private const T[] equals = " = ";
|
|
121 version (Win32)
|
|
122 private const T[] NL = "\r\n";
|
|
123 version (Posix)
|
|
124 private const T[] NL = "\n";
|
|
125
|
|
126 /***********************************************************************
|
|
127
|
|
128 Propagate ctor to superclass
|
|
129
|
|
130 ***********************************************************************/
|
|
131
|
|
132 this (OutputStream stream, T[] newline = NL)
|
|
133 {
|
|
134 super (output = Buffer.share (stream));
|
|
135 }
|
|
136
|
|
137 /***********************************************************************
|
|
138
|
|
139 Buffered interface
|
|
140
|
|
141 ***********************************************************************/
|
|
142
|
|
143 final IBuffer buffer ()
|
|
144 {
|
|
145 return output;
|
|
146 }
|
|
147
|
|
148 /***********************************************************************
|
|
149
|
|
150 Write name & value to the provided stream
|
|
151
|
|
152 ***********************************************************************/
|
|
153
|
|
154 final MapOutput append (T[] name, T[] value)
|
|
155 {
|
|
156 output (name) (equals) (value) (NL);
|
|
157 return this;
|
|
158 }
|
|
159
|
|
160 /***********************************************************************
|
|
161
|
|
162 Write AA properties to the provided stream
|
|
163
|
|
164 ***********************************************************************/
|
|
165
|
|
166 final MapOutput append (T[][T[]] properties)
|
|
167 {
|
|
168 foreach (key, value; properties)
|
|
169 append (key, value);
|
|
170 return this;
|
|
171 }
|
|
172 }
|
|
173
|
|
174
|
|
175
|
|
176 /*******************************************************************************
|
|
177
|
|
178 *******************************************************************************/
|
|
179
|
|
180 debug (UnitTest)
|
|
181 {
|
|
182 import tango.io.Stdout;
|
|
183 import tango.io.GrowBuffer;
|
|
184
|
|
185 unittest
|
|
186 {
|
|
187 auto buf = new GrowBuffer;
|
|
188 auto input = new MapInput!(char)(buf);
|
|
189 auto output = new MapOutput!(char)(buf);
|
|
190
|
|
191 char[][char[]] map;
|
|
192 map["foo"] = "bar";
|
|
193 map["foo2"] = "bar2";
|
|
194 output.append (map);
|
|
195
|
|
196 map = map.init;
|
|
197 input.load (map);
|
|
198 assert (map["foo"] == "bar");
|
|
199 assert (map["foo2"] == "bar2");
|
|
200 }
|
|
201 }
|