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