Mercurial > projects > mde
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mde/mergetag/write.d Sat Oct 27 18:05:39 2007 +0100 @@ -0,0 +1,205 @@ +/************************************************************************************************** + * This module contains all writing functions, for both binary and text MergeTag files. + * + * Files can be written in a text or binary form; binary is faster and smaller while text allows + * editing with an ordinary text editor. TextWriter and BinaryWriter are the main classes, both of + * which implement the interface IWriter. DualWriter is another class implementing IWriter, which + * contains a private instance of a TextWriter and a BinaryWriter and implements all methods in the + * interface simply by chaining the appropriate method from each of these classes, thus performing + * two writes at once. + * + * Any of these three classes may be used directly, or makeWriter may be invoked to create an + * instance of the appropriate class. + *************************************************************************************************/ +module mde.mergetag.write; + +// package imports +import mde.mergetag.dataset; +import mde.mergetag.exception; + +// tango imports +import tango.io.FileConduit; +import tango.io.Buffer : Buffer, IBuffer; +import tango.text.convert.Layout : Layout; +import tango.io.Print : Print; +import tango.util.log.Log : Log, Logger; + +Logger logger; +static this () { + logger = Log.getLogger ("mde.mergetag.write"); +} + +/** + * Enumeration for specifying the writing method ("Params" section shows possible values). + * + * Params: + * Unspecified = If the filename ends with one of .mtb or .mtt, the file is written in + * that format. Otherwise a binary mode is assumed. + * Binary = Use binary mode (default extension: .mtb or no extension). + * Text = Use text mode (default extension: .mtt; as with the above it is not automatically added). + * Both = +*/ +enum WriterMethod : byte { + Unspecified = -1, + Binary = 1, + Text = 2, + Both = 3 +} + +/** Method to create and return either a TextWriter or a BinaryWriter, depending primalily on the + * method parameter and using the filename extension as a fallback. + * + * An exception is thrown if neither test can deduce the writing method. + */ +IWriter makeWriter (char[] path, DataSet dataset, WriterMethod method = WriterMethod.Unspecified) { + makeWriter (new FilePath (path), dataset, method); +} +/** ditto */ +IWriter makeWriter (PathView path, DataSet dataset, WriterMethod method = WriterMethod.Unspecified) { + if (method == WriterMethod.Unspecified) { + if (path.ext == "mtt") method = WriterMethod.Text; + else if (path.ext == "mtb") method = WriterMethod.Binary; + else throwMTErr ("Unable to determine writing format: text or binary", new MTFileFormatException); + } + if (method == WriterMethod.Binary) throwMTErr ("Binary writing not supported yet!", new MTFileFormatException); + else if (method == WriterMethod.Text) return new TextWriter (path, dataset); + else if (method == WriterMethod.Both) throwMTErr ("Dual writing not supported yet!", new MTFileFormatException); +} + +/// Interface for methods and data necessarily available in TextWriter and/or BinaryWriter. +scope interface IWriter { + char[][ID] indexTable; // only used by TextWriter, but available in both + + this (char[] path, DataSet dataset_); + this (PathView path, DataSet dataset_); + ~this (); + + void write (); +} + +/+ +scope class BinaryWriter : IWriter +{ +} ++/ + +/** + * Class to write a dataset to a file. + * + * Is a scope class, since the file is kept open until ~this() runs. + */ +scope class TextWriter : IWriter +{ +//BEGIN DATA + /** The container where data is written from. + */ + DataSet dataset; + + + /** A table, which if created, allows items in a text file to be written with a string ID. + * + * If any ID (for a section or tag) to be written is found in this table, the corresponding + * string is written instead. + */ + char[][ID] indexTable; // see setIndexLookupTable() doc for use. + +private: + // taken from tango.io.Console, mostly to make sure notepad can read our files: + version (Win32) + const char[] Eol = "\r\n"; + else + const char[] Eol = "\n"; + + bool fatal = false; // fatal error occured: don't attempt anything else + bool fileOpen = false; // file needs to be closed on exit + bool writtenHeader = false; // The header MUST be written exactly once at the beginning of the file. + + FileConduit conduit; // actual conduit; don't use directly when there's content in the buffer + IBuffer buffer; // write strings directly to this (use opCall(void[]) ) + Print!(char) format; // formats output to buffer +//END DATA + +//BEGIN CTOR / DTOR + /** Tries to open file path for writing. + * + * Params: + * path = The name or FilePath of the file to open. + * Standard extensions are .mtt and .mtb for text and binary files respectively. + * dataset_ = If null create a new DataSet, else use existing DataSet *dataset_ and merge read + * data into it. + */ + public this (char[] path, DataSet dataset_) { + this (new FilePath (path), dataset_); + } + /** ditto */ + public this (PathView path, DataSet dataset_) { + try { // open a conduit on the file + conduit = new FileConduit (path, FileConduit.WriteCreate); + buffer = new Buffer(conduit); + format = new Print!(char) (new Layout!(char), buffer); + fileOpen = true; + } catch (Exception e) { + throwMTErr ("Error opening file: " ~ e.msg); + } + } // OK, all set to start writing. + + ~this () { // close file on exit + if (fileOpen) { + buffer.flush(); + conduit.close(); + } + } +//END CTOR / DTOR + + /** Writes the header and all DataSections. + * + * Firstly writes the header unless it has already been read. Then writes all DataSections + * to the file. Thus write is called more than once with or without changing the DataSet the + * header should be written only once. This behaviour could, for instance, be used to write + * multiple DataSets into one file without firstly merging them. Note that this behaviour may + * be changed when binary support is added. + */ + public void write () + { + // Write the header: + if (!writtenHeader) { + buffer ("{MT")(MTFormatVersion.CurrentString)("}")(Eol); + writtenHeader = true; + } + writeSection (dataset.header); + + // Write the rest: + foreach (ID id, DataSection sec; dataset.sec) { + writeSectionIdentifier (id); + writeSection (sec); + } + + buffer.flush(); + } + + private void writeSectionIdentifier (ID id) { + buffer ("{"); + char[]* p = id in indexTable; // look for a string ID + if (p) buffer ("\"")(*p)("\""); // write a string ID + else format (cast(uint) id); // write a numeric ID + buffer ("}")(Eol); + } + + private void writeSection (DataSection sec) { + + buffer (Eol); // blank line at end of file + } + + private void throwMTErr (char[] msg, Exception exc = new MTException) { + fatal = true; // if anyone catches the error and tries to do anything --- we're dead now + logger.error (msg); // report the error + throw exc; // and signal our error + } +} + +/+ +Implement std CTORs which add extensions to each filename and extra CTORs which take two filenames. +scope class DualWriter : IWriter +{ +} ++/