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