diff mde/mergetag/Writer.d @ 14:0047b364b6d9

Changed much of the mergetag structure and some functionality. First tests on windows. Changes to mergetag Reader methods. New functionality allowing a dataSecCreator to cause sections to be skipped. Moved several of the mergetag modules and some of their contents around. Moved all interfaces to separate modules in iface/ . IReader & IWriter interfaces exist; MTTReader, MTBReader, MTTWriter, MTBWriter & DualWriter all now exist and implement IReader/IWriter (although the MTB variants are dummy classes); makeReader & makeWriter should both be fully functional. Tested building on windows with partial success (works but window won't open). Included a temporary hack from windows to get supported resolutions information. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Fri, 07 Mar 2008 17:51:02 +0000
parents
children 4608be19ebe2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mde/mergetag/Writer.d	Fri Mar 07 17:51:02 2008 +0000
@@ -0,0 +1,269 @@
+/**************************************************************************************************
+ * 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.Writer;
+
+// package imports
+public import mde.mergetag.iface.IWriter;
+import mde.mergetag.DataSet;
+import mde.mergetag.internal;
+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.Writer");
+}
+
+
+/** Method to create and return either a MTTWriter or a MTBWriter.
+ *
+ * Has two modes of operation: if method is FromExtension, examines the existing extension and
+ * creates a MTT/MTB writer if the extension is mtt or mtb (throwing if not).
+ *
+ * Otherwise, writing format is determined directly by method, and appropriate extensions are
+ * added to the file name without checking for an existing extension.
+ *
+ * Params:
+ *  path = File path
+ *  dataset = Dataset passed to Writer to write from (if null, must be set before write() is called)
+ *  method = $(TABLE
+ *      $(TR $(TH Value)            $(TH Writer returned)       $(TH Suffix added))
+ *      $(TR $(TD FromExtension)    $(TD MTBWriter or MTTWriter)$(TD $(I none)))
+ *      $(TR $(TD Binary)           $(TD MTBWriter)             $(TD .mtb))
+ *      $(TR $(TD Text)             $(TD MTTWriter)             $(TD .mtt))
+ *      $(TR $(TD Both)             $(TD DualWriter)            $(TD .mtb / .mtt))
+ *  )
+ *
+ * Throws:
+ *  MTFileFormatException if neither test can deduce the writing method, the supplied writing
+ *  method is invalid or the determined/supplied method is not yet implemented.
+ *
+ * Use as:
+ * -----------------------
+ * DataSet dataset; // contains data to write
+ * IWriter foo;
+ * try {
+ *   foo = makeWriter(...);
+ *   foo.write();
+ * }
+ * catch (MTException) {}
+ * -----------------------
+ * Where the makeWriter line has one of the following forms:
+ * -----------------------
+ *   foo = makeWriter("foo.mtt", dataset);
+ *   foo = makeWriter("foo", dataset, WriterMethod.Text);
+ * -----------------------
+ *
+ * Throws:
+ *  MTFileFormatException if unable to determine writing format or use requested format.
+ */
+IWriter makeWriter (char[] path, DataSet dataset = null, WriterMethod method = WriterMethod.FromExtension) {
+    if (method == WriterMethod.FromExtension) {
+        PathView fpath = new FilePath (path);
+        
+        if (fpath.ext == "mtt") return new MTTWriter (fpath, dataset);
+        else if (fpath.ext == "mtb") return new MTBWriter (fpath, dataset);
+        else {
+            logger.error ("Unable to determine writing format: text or binary");
+            throw new MTFileFormatException;
+        }
+    }
+    else {
+        if (method == WriterMethod.Binary) return new MTBWriter (path~".mtb", dataset);
+        else if (method == WriterMethod.Text) return new MTTWriter (path~".mtt", dataset);
+        else if (method == WriterMethod.Both) return new DualWriter (path, dataset);
+        else throw new MTFileFormatException;
+    }
+}
+
+
+/**
+ * Class to write a dataset to a file.
+ *
+ * Files are only actually open for writing while the write() method is running.
+ *
+ * Throws:
+ *  $(TABLE
+ *  $(TR $(TH Exception) $(TH Thrown when))
+ *  $(TR $(TD MTNoDataSetException) $(TD No dataset is available to write from))
+ *  $(TR $(TD MTFileIOException) $(TD An error occurs while attemting to write the file))
+ *  $(TR $(TD MTException) $(TD An unexpected error occurs))
+ *  )
+ * Note that all exceptions extend MTException; unlike Reader exceptions don't block further calls.
+ */
+class MTTWriter : IWriter
+{
+//BEGIN DATA
+    /// Get or set the DataSet (i.e. the container from which all data is written).
+    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
+    /** Prepares to open file path for writing.
+    *
+    * The call doesn't actually execute any code so cannot fail (unless out of memory).
+    *
+    * 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.
+     */
+    public void write ()
+    {
+        if (!_dataset) throwMTErr ("write(): no Dataset available to write from!", new MTNoDataSetException ());
+        
+        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, IDataSection 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, IDataSection 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 MTBWriter (and move both writers to own modules?).
+*/
+class MTBWriter : IWriter {
+    public this (char[] path, DataSet ds = null) {
+        this (new FilePath (path), ds);
+    }
+    public this (PathView path, DataSet ds = null) {
+        throw new MTNotImplementedException;
+        
+        /+_path = path;
+        _dataset = ds;+/
+    }
+    
+    DataSet dataset () {
+        return null;
+    }
+    void dataset (DataSet) {}
+    
+    void write () {}
+}
+
+/* Basic implementation for mtt only.
+*
+*Implement std CTORs which add extensions to each filename and extra CTORs which take two filenames.
+*/
+class DualWriter : IWriter {
+    /** The individual writers.
+    *
+    * Potentially could be used directly, but I'd suggest not. */
+    MTTWriter mtt;
+    //MTBWriter mtb;  /** ditto */
+    
+    public this (char[] path, DataSet ds = null) {
+        mtt = new MTTWriter (path~".mtt", ds);
+    }
+    
+    DataSet dataset () {
+        return mtt.dataset;
+    }
+    void dataset (DataSet ds) {
+        mtt.dataset = ds;
+    }
+    
+    /** Write.
+    *
+    * Write text then binary, so the mtb file will be the most recent.
+    */
+    void write () {
+        mtt.write();
+    }
+}