Implement the DateTime class

fixes #9839
This commit is contained in:
Gunnar Beutner 2016-03-29 12:45:22 +02:00
parent 12dadfdfb3
commit eb0892273e
17 changed files with 327 additions and 45 deletions

View File

@ -17,6 +17,7 @@
mkclass_target(application.ti application.tcpp application.thpp)
mkclass_target(configobject.ti configobject.tcpp configobject.thpp)
mkclass_target(datetime.ti datetime.tcpp datetime.thpp)
mkclass_target(filelogger.ti filelogger.tcpp filelogger.thpp)
mkclass_target(logger.ti logger.tcpp logger.thpp)
mkclass_target(streamlogger.ti streamlogger.tcpp streamlogger.thpp)
@ -25,7 +26,7 @@ mkclass_target(sysloglogger.ti sysloglogger.tcpp sysloglogger.thpp)
set(base_SOURCES
application.cpp application.thpp application-version.cpp array.cpp
array-script.cpp boolean.cpp boolean-script.cpp console.cpp context.cpp
convert.cpp debuginfo.cpp dictionary.cpp dictionary-script.cpp
convert.cpp datetime.cpp datetime.thpp datetime-script.cpp debuginfo.cpp dictionary.cpp dictionary-script.cpp
configobject.cpp configobject.thpp configobject-script.cpp configtype.cpp configwriter.cpp dependencygraph.cpp
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp initialize.cpp json.cpp
json-script.cpp loader.cpp logger.cpp logger.thpp math-script.cpp

View File

@ -18,6 +18,7 @@
******************************************************************************/
#include "base/convert.hpp"
#include "base/datetime.hpp"
#include <boost/lexical_cast.hpp>
using namespace icinga;
@ -31,3 +32,18 @@ String Convert::ToString(const Value& val)
{
return val;
}
double Convert::ToDateTimeValue(double val)
{
return val;
}
double Convert::ToDateTimeValue(const Value& val)
{
if (val.IsNumber())
return val;
else if (val.IsObjectType<DateTime>())
return static_cast<DateTime::Ptr>(val)->GetValue();
else
BOOST_THROW_EXCEPTION(std::invalid_argument("Not a DateTime value."));
}

View File

@ -83,6 +83,9 @@ public:
static String ToString(const String& val);
static String ToString(const Value& val);
static double ToDateTimeValue(double val);
static double ToDateTimeValue(const Value& val);
private:
Convert(void);
};

View File

@ -0,0 +1,47 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "base/datetime.hpp"
#include "base/function.hpp"
#include "base/functionwrapper.hpp"
#include "base/scriptframe.hpp"
#include "base/objectlock.hpp"
using namespace icinga;
static String DateTimeFormat(const String& format)
{
ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
DateTime::Ptr self = static_cast<DateTime::Ptr>(vframe->Self);
return self->Format(format);
}
Object::Ptr DateTime::GetPrototype(void)
{
static Dictionary::Ptr prototype;
if (!prototype) {
prototype = new Dictionary();
prototype->Set("format", new Function(WrapFunction(DateTimeFormat)));
}
return prototype;
}

75
lib/base/datetime.cpp Normal file
View File

@ -0,0 +1,75 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "base/datetime.hpp"
#include "base/datetime.tcpp"
#include "base/utility.hpp"
#include "base/primitivetype.hpp"
using namespace icinga;
REGISTER_TYPE_WITH_PROTOTYPE(DateTime, DateTime::GetPrototype());
DateTime::DateTime(double value)
: m_Value(value)
{ }
DateTime::DateTime(const std::vector<Value>& args)
{
if (args.empty())
m_Value = Utility::GetTime();
else if (args.size() == 3 || args.size() == 6) {
struct tm tms;
tms.tm_year = args[0] - 1900;
tms.tm_mon = args[1] - 1;
tms.tm_mday = args[2];
if (args.size() == 6) {
tms.tm_hour = args[3];
tms.tm_min = args[4];
tms.tm_sec = args[5];
} else {
tms.tm_hour = 0;
tms.tm_min = 0;
tms.tm_sec = 0;
}
tms.tm_isdst = -1;
m_Value = mktime(&tms);
} else if (args.size() == 1)
m_Value = args[0];
else
BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid number of arguments for the DateTime constructor."));
}
double DateTime::GetValue(void) const
{
return m_Value;
}
String DateTime::Format(const String& format) const
{
return Utility::FormatDateTime(format.CStr(), m_Value);
}
String DateTime::ToString(void) const
{
return Format("%Y-%m-%d %H:%M:%S %z");
}

57
lib/base/datetime.hpp Normal file
View File

@ -0,0 +1,57 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#ifndef DATETIME_H
#define DATETIME_H
#include "base/i2-base.hpp"
#include "base/datetime.thpp"
#include "base/value.hpp"
#include <vector>
namespace icinga
{
/**
* A date/time value.
*
* @ingroup base
*/
class I2_BASE_API DateTime : public ObjectImpl<DateTime>
{
public:
DECLARE_OBJECT(DateTime);
DateTime(double value);
DateTime(const std::vector<Value>& args);
String Format(const String& format) const;
virtual double GetValue(void) const override;
virtual String ToString(void) const override;
static Object::Ptr GetPrototype(void);
private:
double m_Value;
};
}
#endif /* DATETIME_H */

32
lib/base/datetime.ti Normal file
View File

@ -0,0 +1,32 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
library base;
namespace icinga
{
vararg_constructor class DateTime
{
[state, no_storage] double value {
get;
};
};
}

View File

@ -25,6 +25,7 @@
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <vector>
using boost::intrusive_ptr;
using boost::dynamic_pointer_cast;
@ -61,15 +62,29 @@ extern I2_BASE_API Value Empty;
IMPL_TYPE_LOOKUP();
template<typename T>
intrusive_ptr<Object> DefaultObjectFactory(void)
intrusive_ptr<Object> DefaultObjectFactory(const std::vector<Value>& args)
{
if (!args.empty())
BOOST_THROW_EXCEPTION(std::invalid_argument("Constructor does not take any arguments."));
return new T();
}
typedef intrusive_ptr<Object> (*ObjectFactory)(void);
template<typename T>
intrusive_ptr<Object> DefaultObjectFactoryVA(const std::vector<Value>& args)
{
return new T(args);
}
typedef intrusive_ptr<Object> (*ObjectFactory)(const std::vector<Value>&);
template<typename T, bool VA>
struct TypeHelper
{
};
template<typename T>
struct TypeHelper
struct TypeHelper<T, false>
{
static ObjectFactory GetFactory(void)
{
@ -77,6 +92,15 @@ struct TypeHelper
}
};
template<typename T>
struct TypeHelper<T, true>
{
static ObjectFactory GetFactory(void)
{
return DefaultObjectFactoryVA<T>;
}
};
/**
* Base class for all heap-allocated objects. At least one of its methods
* has to be virtual for RTTI to work.

View File

@ -75,6 +75,9 @@ private:
#define REGISTER_PRIMITIVE_TYPE(type, base, prototype) \
REGISTER_PRIMITIVE_TYPE_FACTORY(type, base, prototype, DefaultObjectFactory<type>)
#define REGISTER_PRIMITIVE_TYPE_VA(type, base, prototype) \
REGISTER_PRIMITIVE_TYPE_FACTORY(type, base, prototype, DefaultObjectFactoryVA<type>)
#define REGISTER_PRIMITIVE_TYPE_NOINST(type, base, prototype) \
REGISTER_PRIMITIVE_TYPE_FACTORY(type, base, prototype, NULL)

View File

@ -67,14 +67,14 @@ String Type::GetPluralName(void) const
return name + "s";
}
Object::Ptr Type::Instantiate(void) const
Object::Ptr Type::Instantiate(const std::vector<Value>& args) const
{
ObjectFactory factory = GetFactory();
if (!factory)
BOOST_THROW_EXCEPTION(std::runtime_error("Type does not have a factory function."));
return factory();
return factory(args);
}
bool Type::IsAbstract(void) const

View File

@ -86,7 +86,7 @@ public:
String GetPluralName(void) const;
Object::Ptr Instantiate(void) const;
Object::Ptr Instantiate(const std::vector<Value>& args = std::vector<Value>()) const;
bool IsAssignableFrom(const Type::Ptr& other) const;

View File

@ -20,6 +20,8 @@
#include "base/value.hpp"
#include "base/array.hpp"
#include "base/dictionary.hpp"
#include "base/datetime.hpp"
#include "base/convert.hpp"
#include "base/utility.hpp"
#include "base/objectlock.hpp"
#include <boost/foreach.hpp>
@ -177,6 +179,13 @@ bool Value::operator==(const Value& rhs) const
return false;
if (IsObject()) {
if (IsObjectType<DateTime>() && rhs.IsObjectType<DateTime>()) {
DateTime::Ptr dt1 = *this;
DateTime::Ptr dt2 = rhs;
return dt1->GetValue() == dt2->GetValue();
}
if (IsObjectType<Array>() && rhs.IsObjectType<Array>()) {
Array::Ptr arr1 = *this;
Array::Ptr arr2 = rhs;
@ -234,6 +243,8 @@ Value icinga::operator+(const Value& lhs, const Value& rhs)
return static_cast<String>(lhs) + static_cast<String>(rhs);
else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return static_cast<double>(lhs) + static_cast<double>(rhs);
else if (lhs.IsObjectType<DateTime>() && rhs.IsNumber())
return new DateTime(Convert::ToDateTimeValue(lhs) + rhs);
else if ((lhs.IsObjectType<Array>() || lhs.IsEmpty()) && (rhs.IsObjectType<Array>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty())) {
Array::Ptr result = new Array();
if (!lhs.IsEmpty())
@ -277,6 +288,12 @@ Value icinga::operator-(const Value& lhs, const Value& rhs)
{
if ((lhs.IsNumber() || lhs.IsEmpty()) && !lhs.IsString() && (rhs.IsNumber() || rhs.IsEmpty()) && !rhs.IsString() && !(lhs.IsEmpty() && rhs.IsEmpty()))
return static_cast<double>(lhs) - static_cast<double>(rhs);
else if (lhs.IsObjectType<DateTime>() && rhs.IsNumber())
return new DateTime(Convert::ToDateTimeValue(lhs) - rhs);
else if (lhs.IsObjectType<DateTime>() && rhs.IsObjectType<DateTime>())
return Convert::ToDateTimeValue(lhs) - Convert::ToDateTimeValue(rhs);
else if ((lhs.IsObjectType<DateTime>() || lhs.IsEmpty()) && (rhs.IsObjectType<DateTime>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return new DateTime(Convert::ToDateTimeValue(lhs) - Convert::ToDateTimeValue(rhs));
else if ((lhs.IsObjectType<Array>() || lhs.IsEmpty()) && (rhs.IsObjectType<Array>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty())) {
if (lhs.IsEmpty())
return new Array();
@ -567,6 +584,8 @@ bool icinga::operator<(const Value& lhs, const Value& rhs)
return static_cast<String>(lhs) < static_cast<String>(rhs);
else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return static_cast<double>(lhs) < static_cast<double>(rhs);
else if ((lhs.IsObjectType<DateTime>() || lhs.IsEmpty()) && (rhs.IsObjectType<DateTime>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return Convert::ToDateTimeValue(lhs) < Convert::ToDateTimeValue(rhs);
else
BOOST_THROW_EXCEPTION(std::invalid_argument("Operator < cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
}
@ -597,6 +616,8 @@ bool icinga::operator>(const Value& lhs, const Value& rhs)
return static_cast<String>(lhs) > static_cast<String>(rhs);
else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return static_cast<double>(lhs) > static_cast<double>(rhs);
else if ((lhs.IsObjectType<DateTime>() || lhs.IsEmpty()) && (rhs.IsObjectType<DateTime>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return Convert::ToDateTimeValue(lhs) > Convert::ToDateTimeValue(rhs);
else
BOOST_THROW_EXCEPTION(std::invalid_argument("Operator > cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
}
@ -627,6 +648,8 @@ bool icinga::operator<=(const Value& lhs, const Value& rhs)
return static_cast<String>(lhs) <= static_cast<String>(rhs);
else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return static_cast<double>(lhs) <= static_cast<double>(rhs);
else if ((lhs.IsObjectType<DateTime>() || lhs.IsEmpty()) && (rhs.IsObjectType<DateTime>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return Convert::ToDateTimeValue(lhs) <= Convert::ToDateTimeValue(rhs);
else
BOOST_THROW_EXCEPTION(std::invalid_argument("Operator <= cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
}
@ -657,6 +680,8 @@ bool icinga::operator>=(const Value& lhs, const Value& rhs)
return static_cast<String>(lhs) >= static_cast<String>(rhs);
else if ((lhs.IsNumber() || lhs.IsEmpty()) && (rhs.IsNumber() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return static_cast<double>(lhs) >= static_cast<double>(rhs);
else if ((lhs.IsObjectType<DateTime>() || lhs.IsEmpty()) && (rhs.IsObjectType<DateTime>() || rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()) && !(lhs.IsEmpty() && rhs.IsEmpty()))
return Convert::ToDateTimeValue(lhs) >= Convert::ToDateTimeValue(rhs);
else
BOOST_THROW_EXCEPTION(std::invalid_argument("Operator >= cannot be applied to values of type '" + lhs.GetTypeName() + "' and '" + rhs.GetTypeName() + "'"));
}

View File

@ -424,15 +424,15 @@ ExpressionResult FunctionCallExpression::DoEvaluate(ScriptFrame& frame, DebugHin
}
if (vfunc.IsObjectType<Type>()) {
if (m_Args.empty())
return VMOps::ConstructorCall(vfunc, m_DebugInfo);
else if (m_Args.size() == 1) {
ExpressionResult argres = m_Args[0]->Evaluate(frame);
std::vector<Value> arguments;
BOOST_FOREACH(Expression *arg, m_Args) {
ExpressionResult argres = arg->Evaluate(frame);
CHECK_RESULT(argres);
return VMOps::CopyConstructorCall(vfunc, argres.GetValue(), m_DebugInfo);
} else
BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor.", m_DebugInfo));
arguments.push_back(argres.GetValue());
}
return VMOps::ConstructorCall(vfunc, arguments, m_DebugInfo);
}
if (!vfunc.IsObjectType<Function>())

View File

@ -55,36 +55,33 @@ public:
return ScriptGlobal::Get(name);
}
static inline Value CopyConstructorCall(const Type::Ptr& type, const Value& value, const DebugInfo& debugInfo = DebugInfo())
static inline Value ConstructorCall(const Type::Ptr& type, const std::vector<Value>& args, const DebugInfo& debugInfo = DebugInfo())
{
if (type->GetName() == "String")
return Convert::ToString(value);
else if (type->GetName() == "Number")
return Convert::ToDouble(value);
else if (type->GetName() == "Boolean")
return Convert::ToBool(value);
else if (!value.IsEmpty() && !type->IsAssignableFrom(value.GetReflectionType()))
BOOST_THROW_EXCEPTION(ScriptError("Invalid cast: Tried to cast object of type '" + value.GetReflectionType()->GetName() + "' to type '" + type->GetName() + "'", debugInfo));
if (type->GetName() == "String") {
if (args.empty())
return "";
else if (args.size() == 1)
return Convert::ToString(args[0]);
else
BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor."));
} else if (type->GetName() == "Number") {
if (args.empty())
return 0;
else if (args.size() == 1)
return Convert::ToDouble(args[0]);
else
BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor."));
} else if (type->GetName() == "Boolean") {
if (args.empty())
return 0;
else if (args.size() == 1)
return Convert::ToBool(args[0]);
else
BOOST_THROW_EXCEPTION(ScriptError("Too many arguments for constructor."));
} else if (args.size() == 1 && type->IsAssignableFrom(args[0].GetReflectionType()))
return args[0];
else
return value;
}
static inline Value ConstructorCall(const Type::Ptr& type, const DebugInfo& debugInfo = DebugInfo())
{
if (type->GetName() == "String")
return "";
else if (type->GetName() == "Number")
return 0;
else if (type->GetName() == "Boolean")
return false;
else {
Object::Ptr object = type->Instantiate();
if (!object)
BOOST_THROW_EXCEPTION(ScriptError("Failed to instantiate object of type '" + type->GetName() + "'", debugInfo));
return object;
}
return type->Instantiate(args);
}
static inline Value FunctionCall(ScriptFrame& frame, const Value& self, const Function::Ptr& func, const std::vector<Value>& arguments)

View File

@ -137,6 +137,7 @@ code { return T_CODE; }
load_after { return T_LOAD_AFTER; }
library { return T_LIBRARY; }
abstract { yylval->num = TAAbstract; return T_CLASS_ATTRIBUTE; }
vararg_constructor { yylval->num = TAVarArgConstructor; return T_CLASS_ATTRIBUTE; }
config { yylval->num = FAConfig; return T_FIELD_ATTRIBUTE; }
state { yylval->num = FAState; return T_FIELD_ATTRIBUTE; }
enum { yylval->num = FAEnum; return T_FIELD_ATTRIBUTE; }

View File

@ -222,7 +222,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
/* TypeHelper */
if (klass.Attributes & TAAbstract) {
m_Header << "template<>" << std::endl
<< "struct TypeHelper<" << klass.Name << ">" << std::endl
<< "struct TypeHelper<" << klass.Name << ", " << ((klass.Attributes & TAVarArgConstructor) ? "true" : "false") << ">" << std::endl
<< "{" << std::endl
<< "\t" << "static ObjectFactory GetFactory(void)" << std::endl
<< "\t" << "{" << std::endl
@ -412,7 +412,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
m_Impl << "ObjectFactory TypeImpl<" << klass.Name << ">::GetFactory(void) const" << std::endl
<< "{" << std::endl
<< "\t" << "return TypeHelper<" << klass.Name << ">::GetFactory();" << std::endl
<< "\t" << "return TypeHelper<" << klass.Name << ", " << ((klass.Attributes & TAVarArgConstructor) ? "true" : "false") << ">::GetFactory();" << std::endl
<< "}" << std::endl << std::endl;
/* GetLoadDependencies */

View File

@ -159,7 +159,8 @@ struct Field
enum TypeAttribute
{
TAAbstract = 1
TAAbstract = 1,
TAVarArgConstructor = 2
};
struct Klass