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 +/