Mercurial > projects > mde
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(); + } +}