Mercurial > projects > mde
view mde/mergetag/dataset.d @ 7:b544c3a7c9ca
Some changes to exceptions and a few more debug commands.
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Wed, 16 Jan 2008 12:48:07 +0000 |
parents | dcb24afa0dce |
children | f63f4f41a2dc |
line wrap: on
line source
/// This module contains a minimal definition of a MergeTag DataSet. module mde.mergetag.dataset; // package imports import mde.mergetag.exception; // other mde imports import mde.text.util; import mde.text.parse : parse; import mde.text.format : format; // tango imports import Util = tango.text.Util; import tango.util.log.Log : Log, Logger; /** Typedef for data & section indexes (can be changed to ulong if necessary.) */ typedef uint ID; package struct MTFormatVersion { enum VERS : ubyte { // convenient list of all known file format versions INVALID = 0x00, MT01 = 0x01, // not yet final } /// The current MergeTag version static const VERS Current = VERS.MT01; static const char[2] CurrentString = "01"; static VERS parseString (char[] str) in { assert (str.length == 2); } body { if (str[0] == '0' && str[1] == '1') return VERS.MT01; else return VERS.INVALID; } } private Logger logger; static this () { logger = Log.getLogger ("mde.mergetag.dataset"); } struct TextTag { TextTag opCall (char[] _tp, ID _id, char[] _dt) { TextTag ret; ret.tp = _tp; ret.id = _id; ret.dt = _dt; return ret; } char[] tp, dt; ID id; } /************************************************************************************************** * Data class; contains a DataSection class instance for each loaded section of a file. * * Any class implementing DataSection may be used to store data; by default a DefaultData class is * used when reading a file. Another class may be used by creating the sections before reading the * file or passing the reader a function to create the sections (see Reader.dataSecCreator). * * Could be a struct, except that structs are value types (not reference types). */ class DataSet { DefaultData header; /// Header DataSection[ID] sec; /// Dynamic array of sections /// Return a reference to the indexed item DataSection opIndex(ID i) { return sec[i]; } /// Template to return all sections of a child-class type. T[ID] getSections (T : DataSection) () { T[ID] ret; foreach (ID id, DataSection s; sec) { T x = cast(T) s; if (x) ret[id] = x; // if non-null } return ret; } } /** * Interface for data storage classes, which contain all data-tags loaded from a single section of a * file. * * A class implementing this may implement the addTag function to do whatever it likes with the * data passed; DefaultData separates this data out into supported types and stores it * appropriately, while throwing an error when unsupported types are passed, but a different * implementation could filter out the tags desired and use them directly, while ignoring the rest. * The parse module provides a useful set of templated functions to * convert the data accordingly. It is advised to keep the type definitions as defined in the file- * format except for user-defined types. * * Another idea for a DataSection class: * Use a void*[ID] variable to store all data (may also need a type var for each item). * addTag should call a templated function which calls parse then casts to a void* and stores the data. * Use a templated get(T)(ID) method which checks the type and casts to T. */ interface DataSection { typedef void delegate (char[],ID,char[]) ItemDelg; /** Handles parsing of data items for all recognised types. * * Should throw an MTUnknownTypeException for unsupported types with a short explanation of the * form: "Package ClassName: supported types" or similar. * * Only MTUnknownTypeException and MTaddTagParseException exceptions are caught by a reader * calling addTag. */ void addTag (char[],ID,char[]); void writeAll (ItemDelg); /// TBD debug void debugFunc (); /// Run in debug builds after parseSection. } /** * Default DataSection class. * * Supports all the basic types currently supported and array versions of * each (except no arrays of binary or string types; these are already arrays). * Doesn't support custom types, but inheriting classes may add support. */ /* Note: I wrote this comment when the code looked rather worse. It's still partially applicable though. * * Due to a failure to use generic programming techniques for most of this (maybe because it's not * possible or maybe just because I don't know how to use templates properly) a lot of this code is * really horrible and has to refer to EVERY data member. * Be really careful if you add any items to this class. * * I really don't like having to do things like this, but it provides a lot of benefits such as no * need to store types and no need to check an argument's type for every access (this could be done * by throwing errors, but then an incorrect (perhaps hand-edited) data file could cause a lot of * errors to be thrown). */ class DefaultData : DataSection { //BEGIN DATA /** Data Members * * These names are available for direct access. * * An alternative access method is to use the provided templates: * -------------------- * template Arg(T) { * alias Name Arg; * } * -------------------- * * Use with a mixin or directly: * -------------------- * mixin Arg!(type); * auto x = Arg; * * type y = Arg!(type).Arg; * -------------------- * Note: trying to use Arg!(type) to implicitly refer to Arg!(type).Arg causes compiler errors due to * --- alias Name Arg; --- * actually being a mixin. */ bool [ID] _bool; byte [ID] _byte; /// ditto short [ID] _short; /// ditto int [ID] _int; /// ditto long [ID] _long; /// ditto ubyte [ID] _ubyte; /// ditto ushort [ID] _ushort; /// ditto uint [ID] _uint; /// ditto ulong [ID] _ulong; /// ditto char [ID] _char; /// ditto float [ID] _float; /// ditto double [ID] _double; /// ditto real [ID] _real; /// ditto bool[] [ID] _boolA; /// ditto byte[] [ID] _byteA; /// ditto short[] [ID] _shortA; /// ditto int[] [ID] _intA; /// ditto long[] [ID] _longA; /// ditto ubyte[] [ID] _ubyteA; /// ditto ushort[] [ID] _ushortA; /// ditto uint[] [ID] _uintA; /// ditto ulong[] [ID] _ulongA; /// ditto char[] [ID] _charA; /// ditto float[] [ID] _floatA; /// ditto double[] [ID] _doubleA; /// ditto real[] [ID] _realA; /// ditto /** Alias names */ alias _ubyteA _binary; alias _charA _string; /// ditto //END DATA void addTag (char[] tp, ID id, char[] dt) { /// Supports all standard types. if (tp.length == 0) throw new MTUnknownTypeException; // split list up a bit for performance: if (tp[0] < 'l') { if (tp[0] < 'd') { mixin ( `if (tp == "binary") addTag_add!(ubyte[]) (id, dt);` ~ addTag_elifIsType_add!(bool) ~ addTag_elifIsType_add!(bool[]) ~ addTag_elifIsType_add!(byte) ~ addTag_elifIsType_add!(byte[]) ~ addTag_elifIsType_add!(char) ~ addTag_elifIsType_add!(char[]) ~ `else throw new MTUnknownTypeException;` ); } else { mixin ( `if (tp == "double") addTag_add!(double) (id, dt);` ~ addTag_elifIsType_add!(double[]) ~ addTag_elifIsType_add!(float) ~ addTag_elifIsType_add!(float[]) ~ addTag_elifIsType_add!(int) ~ addTag_elifIsType_add!(int[]) ~ `else throw new MTUnknownTypeException;` ); } } else { if (tp[0] < 'u') { mixin ( `if (tp == "long") addTag_add!(long) (id, dt);` ~ addTag_elifIsType_add!(long[]) ~ addTag_elifIsType_add!(real) ~ addTag_elifIsType_add!(real[]) ~ addTag_elifIsType_add!(short) ~ addTag_elifIsType_add!(short[]) ~ `else if (tp == "string") addTag_add!(char[]) (id, dt);` ~ `else throw new MTUnknownTypeException;` ); } else { mixin ( `if (tp == "ubyte") addTag_add!(ubyte) (id, dt);` ~ addTag_elifIsType_add!(ubyte[]) ~ addTag_elifIsType_add!(ushort) ~ addTag_elifIsType_add!(ushort[]) ~ addTag_elifIsType_add!(uint) ~ addTag_elifIsType_add!(uint[]) ~ addTag_elifIsType_add!(ulong) ~ addTag_elifIsType_add!(ulong[]) ~ `else throw new MTUnknownTypeException;` ); } } // try-catch block removed (caught by read) } private template addTag_elifIsType_add(T) { const addTag_elifIsType_add = `else if (tp == "`~T.stringof~`")` `addTag_add!(`~T.stringof~`) (id, dt);` ; } private void addTag_add(T) (ID id, char[] dt) { Arg!(T).Arg[id] = parse!(T) (dt); } void writeAll (ItemDelg itemdlg) { foreach (id, dt; _charA) itemdlg ("char[]", id, format!(char[])(dt)); } debug void debugFunc () {} /* These make no attempt to check Arg is valid. * But if the symbol doesn't exist the complier will throw an error anyway, e.g.: * Error: identifier '_boolAA' is not defined */ template Arg(T : T[]) { const ArgString = Arg!(T).ArgString ~ `A`; mixin(`alias `~ArgString~` Arg;`); } template Arg(T) { const ArgString = `_` ~ T.stringof; mixin(`alias `~ArgString~` Arg;`); } } /+class DynamicData : DataSection { void*[TypeInfo] data; }+/ /+ class TemplateData : DataSection { void addTag (char[] tp, ID id, char[] dt) { // runtime deduction of tp and aliasing? // CANNOT add data at runtime though. } // will this work? no idea. // templates can't be used to add non-static elements, so use a static array at index: this template Data(T,TemplateData* p) { static T[ID][TemplateData*] Data; } } +/ unittest { // Only covers DataSet really. DataSet ds = new DataSet; ds.sec[1] = new DefaultData; assert (ds.getSections!(DefaultData)().length == 1); ds.sec[1].addTag ("int",0," -543 "); assert (ds.getSections!(DefaultData)()[1]._int[0] == -543); }