Mercurial > projects > mde
view 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 source
/************************************************************************************************** * 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 { } +/