Files
2025-02-07 19:12:13 +03:30

401 lines
17 KiB
Plaintext

//+------------------------------------------------------------------+
//| IndexMapT.mqh |
//| https://www.mql5.com/en/articles/5706/ |
//+------------------------------------------------------------------+
#define EMPTY ((int)EMPTY_VALUE)
#ifdef HASHMAP_WARNING
#define NULL_PLACEHOLDER "n/a"
#else
#define NULL_PLACEHOLDER ""
#endif
class Object
{
public:
virtual string asString() const
{
return(__FUNCSIG__);
}
virtual string asCSVString() const
{
return(__FUNCSIG__);
}
virtual string getTypeName() const = 0;
};
/**
* Base container for indexed map. Can contain plain types and pointers.
*/
class Container: public Object
{
protected:
enum datatype
{
null,
s,
d,
t,
i,
o,
u
};
datatype type;
public:
datatype getType() const
{
return type;
}
// helper method to access plain type values from the base class
template<typename R>
R get() const;
// helper method to access object pointers from the base class
template<typename R>
Object *getObject() const;
};
/**
* Variable type data for indexed map; plain types only.
*/
template<typename T>
class TypeContainer: public Container
{
private:
int digits;
int flags;
protected:
T v;
TypeContainer()
{
digits = _Digits;
flags = TIME_DATE | TIME_MINUTES;
}
public:
TypeContainer(T _v, int precision = INT_MIN, int timeflags = TIME_DATE | TIME_MINUTES)
{
v = _v;
digits = precision == INT_MIN ? _Digits : precision;
flags = timeflags;
if(typename(T) == "string")
{
type = datatype::s;
}
else
if(typename(T) == "double" || typename(T) == "float")
{
type = datatype::d;
}
else
if(typename(T) == "datetime")
{
type = datatype::t;
}
else
if(typename(T) == "char" || typename(T) == "short" || typename(T) == "int" || typename(T) == "long")
{
type = datatype::i;
}
else
{
type = datatype::u;
}
}
virtual T getValue() const
{
return v;
}
// represent data as a string, convert if necessary
virtual string asString() const
{
switch(type)
{
case datatype::s: return (string)v;
case datatype::d: return DoubleToString((double)v, digits);
case datatype::t: return TimeToString((datetime)v, flags);
case datatype::i: return IntegerToString((long)v);
default: return (string)v;
}
}
virtual string asCSVString() const
{
return asString();
}
virtual string getTypeName() const override
{
return typename(this);
}
};
/**
* Object pointer types for indexed map.
* Pointer will be deleted automatically.
*/
template<typename T>
class ObjectContainer: public Container
{
protected:
Object *o;
public:
ObjectContainer(T _v)
{
o = _v;
type = datatype::o;
}
~ObjectContainer()
{
if(CheckPointer(o) == POINTER_DYNAMIC)
{
delete(o);
}
}
T getObject() const
{
return o;
}
virtual string asString() const override
{
return o.asString();
}
virtual string asCSVString() const
{
return o.asCSVString();
}
virtual string getTypeName() const override
{
return typename(this);
}
};
template<typename R>
R Container::get() const
{
const TypeContainer<R> *ptr = dynamic_cast<const TypeContainer<R> *>(&this);
if(ptr != NULL) return (R)ptr.getValue();
return (R)NULL;
}
template<typename R> // R is supposed to be an object pointer
Object *Container::getObject() const
{
const ObjectContainer<R> *obj = dynamic_cast<const ObjectContainer<R> *>(&this);
if(obj != NULL) return obj.getObject();
return NULL;
}
/**
* Indexed map with random access by key and index.
*/
template<typename T>
class IndexMapT: public Container // Object
{
private:
T keys[];
Container *values[];
int count;
string id;
uchar delimiter;
public:
IndexMapT(): count(0), delimiter(',') {}
IndexMapT(string obj): id (obj), count(0), delimiter(',') {}
IndexMapT(uchar d): count(0), delimiter(d) {}
~IndexMapT()
{
reset();
}
virtual string getTypeName() const override
{
return typename(this);
}
void add(const T key, Container *value)
{
ArrayResize(keys, count + 1);
ArrayResize(values, count + 1);
keys[count] = key;
values[count] = value;
count++;
}
void reset()
{
for(int i = 0; i < count; i++)
{
if(CheckPointer(values[i]) == POINTER_DYNAMIC)
{
delete(values[i]);
}
}
ArrayResize(keys, 0);
ArrayResize(values, 0);
count = 0;
}
bool isKeyExisting(const T key) const
{
return (getIndex(key) != EMPTY);
}
int getIndex(const T key) const
{
for(int i = 0; i < count; i++)
{
if(keys[i] == key) return(i);
}
#ifdef HASHMAP_VERBOSE
Print(__FUNCSIG__, ": no key=", key);
#endif
return EMPTY;
}
Container *operator[](const int index) const
{
if(index < 0 || index >= count)
{
#ifdef HASHMAP_VERBOSE
Print(__FUNCSIG__, ": index=", index);
#endif
return(NULL);
}
return(GetPointer(values[index]));
}
Container *operator[](const T key) const
{
for(int i = 0; i < count; i++)
{
if(keys[i] == key) return(GetPointer(values[i]));
}
#ifdef HASHMAP_VERBOSE
Print(__FUNCSIG__, ": no key=", key);
#endif
return(NULL);
}
T getKey(const int index) const
{
if(index < 0 || index >= count)
{
Print(__FUNCSIG__, ": index=", index);
}
return(keys[index]);
}
template<typename R>
void setValue(const T key, R value)
{
// NB: implementation specific
// in HTML every attribute can occur only once in a tag,
// all successive assignments are ignored
if(!isKeyExisting(key))
{
set(key, new TypeContainer<R>(value));
}
}
void set(const T key, Container *value)
{
int index = getIndex(key);
if(index != EMPTY)
{
#ifdef HASHMAP_WARNING
Print(__FUNCSIG__, ": overwritten key=", key, ", old value=", (values[index] != NULL ? values[index].asString() : "null"), ", new value=", (value != NULL ? value.asString() : "null"));
#endif
values[index] = value;
}
else
{
add(key, value);
}
}
void set(const T key)
{
int index = getIndex(key);
if(index == EMPTY)
{
add(key, NULL);
}
}
int getSize() const
{
return(count);
}
virtual string asString() const
{
string result = "";
for(int i = 0; i < count; i++)
{
result += (string)keys[i] + "=" + (CheckPointer(values[i]) == POINTER_INVALID ? NULL_PLACEHOLDER : values[i].asString()) + ";";
}
return result;
}
virtual string asCSVString() const
{
string result = "";
string d = CharToString(delimiter);
for(int i = 0; i < count; i++)
{
string v = NULL_PLACEHOLDER;
if(CheckPointer(values[i]) != POINTER_INVALID)
{
v = values[i].asCSVString();
StringReplace(v, d, "");
}
if(i < count - 1)
{
result += v + d;
}
else
{
result += v;
}
}
return result;
}
};
class IndexMap: public IndexMapT<string>
{
public:
IndexMap(): IndexMapT() {}
IndexMap(string obj): IndexMapT(obj) {}
IndexMap(uchar d): IndexMapT(d) {}
string get(const string key)
{
Container *c = this[key];
if(c == NULL) return NULL;
return c.get<string>();
}
};