Mercurial > projects > mde
comparison mde/mergetag/write.d @ 0:d547009c104c
Repository creation.
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sat, 27 Oct 2007 18:05:39 +0100 |
parents | |
children | 18491334a525 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:d547009c104c |
---|---|
1 /************************************************************************************************** | |
2 * This module contains all writing functions, for both binary and text MergeTag files. | |
3 * | |
4 * Files can be written in a text or binary form; binary is faster and smaller while text allows | |
5 * editing with an ordinary text editor. TextWriter and BinaryWriter are the main classes, both of | |
6 * which implement the interface IWriter. DualWriter is another class implementing IWriter, which | |
7 * contains a private instance of a TextWriter and a BinaryWriter and implements all methods in the | |
8 * interface simply by chaining the appropriate method from each of these classes, thus performing | |
9 * two writes at once. | |
10 * | |
11 * Any of these three classes may be used directly, or makeWriter may be invoked to create an | |
12 * instance of the appropriate class. | |
13 *************************************************************************************************/ | |
14 module mde.mergetag.write; | |
15 | |
16 // package imports | |
17 import mde.mergetag.dataset; | |
18 import mde.mergetag.exception; | |
19 | |
20 // tango imports | |
21 import tango.io.FileConduit; | |
22 import tango.io.Buffer : Buffer, IBuffer; | |
23 import tango.text.convert.Layout : Layout; | |
24 import tango.io.Print : Print; | |
25 import tango.util.log.Log : Log, Logger; | |
26 | |
27 Logger logger; | |
28 static this () { | |
29 logger = Log.getLogger ("mde.mergetag.write"); | |
30 } | |
31 | |
32 /** | |
33 * Enumeration for specifying the writing method ("Params" section shows possible values). | |
34 * | |
35 * Params: | |
36 * Unspecified = If the filename ends with one of .mtb or .mtt, the file is written in | |
37 * that format. Otherwise a binary mode is assumed. | |
38 * Binary = Use binary mode (default extension: .mtb or no extension). | |
39 * Text = Use text mode (default extension: .mtt; as with the above it is not automatically added). | |
40 * Both = | |
41 */ | |
42 enum WriterMethod : byte { | |
43 Unspecified = -1, | |
44 Binary = 1, | |
45 Text = 2, | |
46 Both = 3 | |
47 } | |
48 | |
49 /** Method to create and return either a TextWriter or a BinaryWriter, depending primalily on the | |
50 * method parameter and using the filename extension as a fallback. | |
51 * | |
52 * An exception is thrown if neither test can deduce the writing method. | |
53 */ | |
54 IWriter makeWriter (char[] path, DataSet dataset, WriterMethod method = WriterMethod.Unspecified) { | |
55 makeWriter (new FilePath (path), dataset, method); | |
56 } | |
57 /** ditto */ | |
58 IWriter makeWriter (PathView path, DataSet dataset, WriterMethod method = WriterMethod.Unspecified) { | |
59 if (method == WriterMethod.Unspecified) { | |
60 if (path.ext == "mtt") method = WriterMethod.Text; | |
61 else if (path.ext == "mtb") method = WriterMethod.Binary; | |
62 else throwMTErr ("Unable to determine writing format: text or binary", new MTFileFormatException); | |
63 } | |
64 if (method == WriterMethod.Binary) throwMTErr ("Binary writing not supported yet!", new MTFileFormatException); | |
65 else if (method == WriterMethod.Text) return new TextWriter (path, dataset); | |
66 else if (method == WriterMethod.Both) throwMTErr ("Dual writing not supported yet!", new MTFileFormatException); | |
67 } | |
68 | |
69 /// Interface for methods and data necessarily available in TextWriter and/or BinaryWriter. | |
70 scope interface IWriter { | |
71 char[][ID] indexTable; // only used by TextWriter, but available in both | |
72 | |
73 this (char[] path, DataSet dataset_); | |
74 this (PathView path, DataSet dataset_); | |
75 ~this (); | |
76 | |
77 void write (); | |
78 } | |
79 | |
80 /+ | |
81 scope class BinaryWriter : IWriter | |
82 { | |
83 } | |
84 +/ | |
85 | |
86 /** | |
87 * Class to write a dataset to a file. | |
88 * | |
89 * Is a scope class, since the file is kept open until ~this() runs. | |
90 */ | |
91 scope class TextWriter : IWriter | |
92 { | |
93 //BEGIN DATA | |
94 /** The container where data is written from. | |
95 */ | |
96 DataSet dataset; | |
97 | |
98 | |
99 /** A table, which if created, allows items in a text file to be written with a string ID. | |
100 * | |
101 * If any ID (for a section or tag) to be written is found in this table, the corresponding | |
102 * string is written instead. | |
103 */ | |
104 char[][ID] indexTable; // see setIndexLookupTable() doc for use. | |
105 | |
106 private: | |
107 // taken from tango.io.Console, mostly to make sure notepad can read our files: | |
108 version (Win32) | |
109 const char[] Eol = "\r\n"; | |
110 else | |
111 const char[] Eol = "\n"; | |
112 | |
113 bool fatal = false; // fatal error occured: don't attempt anything else | |
114 bool fileOpen = false; // file needs to be closed on exit | |
115 bool writtenHeader = false; // The header MUST be written exactly once at the beginning of the file. | |
116 | |
117 FileConduit conduit; // actual conduit; don't use directly when there's content in the buffer | |
118 IBuffer buffer; // write strings directly to this (use opCall(void[]) ) | |
119 Print!(char) format; // formats output to buffer | |
120 //END DATA | |
121 | |
122 //BEGIN CTOR / DTOR | |
123 /** Tries to open file path for writing. | |
124 * | |
125 * Params: | |
126 * path = The name or FilePath of the file to open. | |
127 * Standard extensions are .mtt and .mtb for text and binary files respectively. | |
128 * dataset_ = If null create a new DataSet, else use existing DataSet *dataset_ and merge read | |
129 * data into it. | |
130 */ | |
131 public this (char[] path, DataSet dataset_) { | |
132 this (new FilePath (path), dataset_); | |
133 } | |
134 /** ditto */ | |
135 public this (PathView path, DataSet dataset_) { | |
136 try { // open a conduit on the file | |
137 conduit = new FileConduit (path, FileConduit.WriteCreate); | |
138 buffer = new Buffer(conduit); | |
139 format = new Print!(char) (new Layout!(char), buffer); | |
140 fileOpen = true; | |
141 } catch (Exception e) { | |
142 throwMTErr ("Error opening file: " ~ e.msg); | |
143 } | |
144 } // OK, all set to start writing. | |
145 | |
146 ~this () { // close file on exit | |
147 if (fileOpen) { | |
148 buffer.flush(); | |
149 conduit.close(); | |
150 } | |
151 } | |
152 //END CTOR / DTOR | |
153 | |
154 /** Writes the header and all DataSections. | |
155 * | |
156 * Firstly writes the header unless it has already been read. Then writes all DataSections | |
157 * to the file. Thus write is called more than once with or without changing the DataSet the | |
158 * header should be written only once. This behaviour could, for instance, be used to write | |
159 * multiple DataSets into one file without firstly merging them. Note that this behaviour may | |
160 * be changed when binary support is added. | |
161 */ | |
162 public void write () | |
163 { | |
164 // Write the header: | |
165 if (!writtenHeader) { | |
166 buffer ("{MT")(MTFormatVersion.CurrentString)("}")(Eol); | |
167 writtenHeader = true; | |
168 } | |
169 writeSection (dataset.header); | |
170 | |
171 // Write the rest: | |
172 foreach (ID id, DataSection sec; dataset.sec) { | |
173 writeSectionIdentifier (id); | |
174 writeSection (sec); | |
175 } | |
176 | |
177 buffer.flush(); | |
178 } | |
179 | |
180 private void writeSectionIdentifier (ID id) { | |
181 buffer ("{"); | |
182 char[]* p = id in indexTable; // look for a string ID | |
183 if (p) buffer ("\"")(*p)("\""); // write a string ID | |
184 else format (cast(uint) id); // write a numeric ID | |
185 buffer ("}")(Eol); | |
186 } | |
187 | |
188 private void writeSection (DataSection sec) { | |
189 | |
190 buffer (Eol); // blank line at end of file | |
191 } | |
192 | |
193 private void throwMTErr (char[] msg, Exception exc = new MTException) { | |
194 fatal = true; // if anyone catches the error and tries to do anything --- we're dead now | |
195 logger.error (msg); // report the error | |
196 throw exc; // and signal our error | |
197 } | |
198 } | |
199 | |
200 /+ | |
201 Implement std CTORs which add extensions to each filename and extra CTORs which take two filenames. | |
202 scope class DualWriter : IWriter | |
203 { | |
204 } | |
205 +/ |