Mercurial > projects > mde
view mde/mergetag/dataset.d @ 2:78eb491bd642
mergetag: partially redesigned dataset and text reader classes. Changed text format.
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sat, 03 Nov 2007 15:15:43 +0000 |
parents | 18491334a525 |
children | 9a990644948c |
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; import mde.text.exception : TextParseException; // 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 { DataSection 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 { /** Handles parsing of data items. * * Should throw an MTUnknownTypeException for unsupported types, after logging to logger. */ void addTag (TypeInfo,ID,char[]); //void writeAll (Print!(char)); /// TBD } /** * 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). */ /* 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 (TypeInfo ti, ID id, char[] dt) { /// for adding tags try { // crazy way of only writing one parameter on each line: mixin ( `if (ti == typeid(bool)) addTag_add!(bool) (id, dt);` ~ addTag_elifIsType_add!(byte) ~ addTag_elifIsType_add!(short) ~ addTag_elifIsType_add!(int) ~ addTag_elifIsType_add!(long) ~ addTag_elifIsType_add!(ubyte) ~ addTag_elifIsType_add!(ushort) ~ addTag_elifIsType_add!(uint) ~ addTag_elifIsType_add!(ulong) ~ addTag_elifIsType_add!(char) ~ addTag_elifIsType_add!(float) ~ addTag_elifIsType_add!(double) ~ addTag_elifIsType_add!(real) ~ addTag_elifIsType_add!(bool[]) ~ addTag_elifIsType_add!(byte[]) ~ addTag_elifIsType_add!(short[]) ~ addTag_elifIsType_add!(int[]) ~ addTag_elifIsType_add!(long[]) ~ addTag_elifIsType_add!(ubyte[]) ~ addTag_elifIsType_add!(ushort[]) ~ addTag_elifIsType_add!(uint[]) ~ addTag_elifIsType_add!(ulong[]) ~ addTag_elifIsType_add!(char[]) ~ addTag_elifIsType_add!(float[]) ~ addTag_elifIsType_add!(double[]) ~ addTag_elifIsType_add!(real[]) ); } catch (TextParseException) { // Just ignore it. A warning's already been logged. } } private template addTag_elifIsType_add(T) { const addTag_elifIsType_add = `else if (ti == typeid(`~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 writeTags (out TextTag[] ret) { //ret.length = Arg!().length + ...; } /* 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 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; } } +/