Mercurial > projects > mde
view mde/mergetag/write.d @ 10:4c3575400769
DefaultData largely rewritten with unittest, SDL input event handling completed with unittest, changes to Init threading.
Init threads now catch own exceptions.
Doc: assigned some inputID devisions.
Added support for remaining SDL events.
Input axes' output is now stored with a short instead of a real.
Input unittest written (for SDL event handling).
Rewrote most of mde.mergetag.defaultdata using generic programming to generate read & write rules for all types. As a direct result, defaultdata can now write properly.
DefaultData unittest written (also provides testing for mergetag read/write).
Moved mde.text.parse/format to tango.scrapple.text.convert.parseTo/parseFrom with many changes.
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Mon, 18 Feb 2008 11:54:56 +0000 |
parents | f63f4f41a2dc |
children | b940f267419e |
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 public import mde.mergetag.dataset; import mde.mergetag.exception; // tango imports import tango.core.Exception; import tango.io.FileConduit; import tango.io.Buffer : Buffer, IBuffer; import tango.io.Print : Print; import convInt = tango.text.convert.Integer; import tango.util.log.Log : Log, Logger; private 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 = null, WriterMethod method = WriterMethod.Unspecified) { return makeWriter (new FilePath (path), dataset, method); } /** ditto */ IWriter makeWriter (PathView path, DataSet dataset = null, WriterMethod method = WriterMethod.Unspecified) { void throwMTErr (char[] msg, Exception exc = new MTException) { logger.error (msg); throw exc; } 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. // FIXME: rename MTWriter or so. interface IWriter { /// Get or set the DataSet. DataSet dataset (); void dataset (DataSet); /// ditto void write (); /// Writing method. } /+ scope class BinaryWriter : IWriter { } +/ /** * Class to write a dataset to a file. * * Files are only actually open for writing while the write() method is running. */ scope class TextWriter : IWriter { //BEGIN DATA /// Get or set the DataSet. DataSet dataset () { return dataset; } void dataset (DataSet ds) /// ditto { dataset = ds; } 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"; /* The container where data is written from. */ DataSet _dataset; PathView path; //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 ds = null) { this (new FilePath (_path), ds); } /** ditto */ public this (PathView _path, DataSet ds = null) { path = _path; _dataset = ds; } //END CTOR / DTOR /** Writes the header and all DataSections. * * Firstly writes the header unless it has already been written. Then writes all DataSections * to the file. Thus if 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. * * Throws: * MTNoDataSetException if the dataset is null, * MTFileIOException if a file IO error occurs, * MTException on any other exception (unexpected). */ public void write () { if (!_dataset) throw new MTNoDataSetException ("write(): Dataset needed to write from!"); try { 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[]) ) // Open a conduit on the file: conduit = new FileConduit (path, FileConduit.WriteCreate); scope(exit) conduit.close(); buffer = new Buffer(conduit); // And a buffer scope(exit) buffer.flush(); // Write the header: buffer ("{MT" ~ MTFormatVersion.CurrentString ~ "}" ~ Eol); if (_dataset.header !is null) writeSection (buffer, _dataset.header); // Write the rest: foreach (ID id, DataSection sec; _dataset.sec) { writeSectionIdentifier (buffer, id); writeSection (buffer, sec); } buffer.flush(); } catch (IOException e) { throwMTErr ("Error writing to file: " ~ e.msg, new MTFileIOException); } catch (Exception e) { throwMTErr ("Unexpected exception when writing file: " ~ e.msg); } } private void writeSectionIdentifier (IBuffer buffer, ID id) { buffer ("{" ~ cast(char[])id ~ "}" ~ Eol); } private void writeSection (IBuffer buffer, DataSection sec) { void writeItem (char[] tp, ID id, char[] dt) { // actually writes an item buffer ("<" ~ tp ~ "|" ~ cast(char[])id ~"=" ~ dt ~ ">" ~ Eol); } sec.writeAll (&writeItem); buffer (Eol); // blank line at end of each section } private void throwMTErr (char[] msg, Exception exc = new MTException) { 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 { } +/