diff --git a/doc/17-language-reference.md b/doc/17-language-reference.md
index f7fae4695..0e48a6c25 100644
--- a/doc/17-language-reference.md
+++ b/doc/17-language-reference.md
@@ -203,6 +203,38 @@ to dereference a reference:
*p = "Hi!"
log(value) // Prints "Hi!" because the variable was changed
+### Namespaces
+
+Namespaces can be used to organize variables and functions. They are used to avoid name conflicts. The `namespace`
+keyword is used to create a new namespace:
+
+ namespace Utils {
+ function calculate() {
+ return 2 + 2
+ }
+ }
+
+The namespace is made available as a global variable which has the namespace's name (e.g. `Utils`):
+
+ Utils.calculate()
+
+The `using` keyword can be used to make all attributes in a namespace available to a script without having to
+explicitly specify the namespace's name for each access:
+
+ using Utils
+ calculate()
+
+The `using` keyword only has an effect for the current file and only for code that follows the keyword:
+
+ calculate() // This will not work.
+ using Utils
+
+The following namespaces are automatically imported as if by using the `using` keyword:
+
+* System
+* Types
+* Icinga
+
### Function Calls
Functions can be called using the `()` operator:
diff --git a/icinga-app/icinga.cpp b/icinga-app/icinga.cpp
index 26ed4cf69..24da88644 100644
--- a/icinga-app/icinga.cpp
+++ b/icinga-app/icinga.cpp
@@ -21,6 +21,7 @@
#include "config/configcompilercontext.hpp"
#include "config/configcompiler.hpp"
#include "config/configitembuilder.hpp"
+#include "config/expression.hpp"
#include "base/application.hpp"
#include "base/logger.hpp"
#include "base/timer.hpp"
@@ -440,7 +441,24 @@ static int Main()
key = define;
value = "1";
}
- ScriptGlobal::Set(key, value);
+
+ std::vector keyTokens = key.Split(".");
+
+ std::unique_ptr expr;
+ std::unique_ptr varExpr{new VariableExpression(keyTokens[0], {}, DebugInfo())};
+ expr = std::move(varExpr);
+
+ for (size_t i = 1; i < keyTokens.size(); i++) {
+ std::unique_ptr indexerExpr{new IndexerExpression(std::move(expr), MakeLiteral(keyTokens[i]))};
+ indexerExpr->SetOverrideFrozen();
+ expr = std::move(indexerExpr);
+ }
+
+ std::unique_ptr setExpr{new SetExpression(std::move(expr), OpSetLiteral, MakeLiteral(value))};
+ setExpr->SetOverrideFrozen();
+
+ ScriptFrame frame(true);
+ setExpr->Evaluate(frame);
}
}
diff --git a/itl/hangman b/itl/hangman
index f4dc118e0..48cd177d7 100644
--- a/itl/hangman
+++ b/itl/hangman
@@ -17,8 +17,8 @@
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
-if (!globals.irc) {
- globals.irc = globals.log
+if (!globals.contains("irc")) {
+ globals.irc = log
}
hm = {
diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt
index e784a3957..3cdc08556 100644
--- a/lib/base/CMakeLists.txt
+++ b/lib/base/CMakeLists.txt
@@ -54,6 +54,7 @@ set(base_SOURCES
math-script.cpp
netstring.cpp netstring.hpp
networkstream.cpp networkstream.hpp
+ namespace.cpp namespace.hpp namespace-script.cpp
number.cpp number.hpp number-script.cpp
object.cpp object.hpp object-script.cpp
objectlock.cpp objectlock.hpp
diff --git a/lib/base/function.hpp b/lib/base/function.hpp
index eae0a0792..493be13ee 100644
--- a/lib/base/function.hpp
+++ b/lib/base/function.hpp
@@ -75,13 +75,29 @@ private:
#define REGISTER_FUNCTION(ns, name, callback, args) \
INITIALIZE_ONCE_WITH_PRIORITY([]() { \
Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), false); \
- ScriptGlobal::Set(#ns "." #name, sf); \
+ Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \
+ nsp->SetAttribute(#name, std::make_shared(sf)); \
}, 10)
#define REGISTER_SAFE_FUNCTION(ns, name, callback, args) \
INITIALIZE_ONCE_WITH_PRIORITY([]() { \
Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), true); \
- ScriptGlobal::Set(#ns "." #name, sf); \
+ Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \
+ nsp->SetAttribute(#name, std::make_shared(sf)); \
+ }, 10)
+
+#define REGISTER_FUNCTION_NONCONST(ns, name, callback, args) \
+ INITIALIZE_ONCE_WITH_PRIORITY([]() { \
+ Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), false); \
+ Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \
+ nsp->SetAttribute(#name, std::make_shared(sf)); \
+ }, 10)
+
+#define REGISTER_SAFE_FUNCTION_NONCONST(ns, name, callback, args) \
+ INITIALIZE_ONCE_WITH_PRIORITY([]() { \
+ Function::Ptr sf = new icinga::Function(#ns "#" #name, callback, String(args).Split(":"), true); \
+ Namespace::Ptr nsp = ScriptGlobal::Get(#ns); \
+ nsp->SetAttribute(#name, std::make_shared(sf)); \
}, 10)
}
diff --git a/lib/base/json-script.cpp b/lib/base/json-script.cpp
index 9df6ee783..d99c71bde 100644
--- a/lib/base/json-script.cpp
+++ b/lib/base/json-script.cpp
@@ -32,11 +32,15 @@ static String JsonEncodeShim(const Value& value)
}
INITIALIZE_ONCE([]() {
- Dictionary::Ptr jsonObj = new Dictionary({
- /* Methods */
- { "encode", new Function("Json#encode", JsonEncodeShim, { "value" }, true) },
- { "decode", new Function("Json#decode", JsonDecode, { "value" }, true) }
- });
+ auto jsonNSBehavior = new ConstNamespaceBehavior();
+ Namespace::Ptr jsonNS = new Namespace(jsonNSBehavior);
- ScriptGlobal::Set("Json", jsonObj);
+ /* Methods */
+ jsonNS->Set("encode", new Function("Json#encode", JsonEncodeShim, { "value" }, true));
+ jsonNS->Set("decode", new Function("Json#decode", JsonDecode, { "value" }, true));
+
+ jsonNSBehavior->Freeze();
+
+ Namespace::Ptr systemNS = ScriptGlobal::Get("System");
+ systemNS->SetAttribute("Json", std::make_shared(jsonNS));
});
diff --git a/lib/base/json.cpp b/lib/base/json.cpp
index c074aed56..27119f434 100644
--- a/lib/base/json.cpp
+++ b/lib/base/json.cpp
@@ -19,6 +19,7 @@
#include "base/json.hpp"
#include "base/debug.hpp"
+#include "base/namespace.hpp"
#include "base/dictionary.hpp"
#include "base/array.hpp"
#include "base/objectlock.hpp"
@@ -39,6 +40,19 @@ typedef unsigned int yajl_size;
typedef size_t yajl_size;
#endif /* YAJL_MAJOR */
+static void EncodeNamespace(yajl_gen handle, const Namespace::Ptr& ns)
+{
+ yajl_gen_map_open(handle);
+
+ ObjectLock olock(ns);
+ for (const Namespace::Pair& kv : ns) {
+ yajl_gen_string(handle, reinterpret_cast(kv.first.CStr()), kv.first.GetLength());
+ Encode(handle, kv.second->Get());
+ }
+
+ yajl_gen_map_close(handle);
+}
+
static void EncodeDictionary(yajl_gen handle, const Dictionary::Ptr& dict)
{
yajl_gen_map_open(handle);
@@ -83,6 +97,13 @@ static void Encode(yajl_gen handle, const Value& value)
case ValueObject:
{
const Object::Ptr& obj = value.Get();
+ Namespace::Ptr ns = dynamic_pointer_cast(obj);
+
+ if (ns) {
+ EncodeNamespace(handle, ns);
+ break;
+ }
+
Dictionary::Ptr dict = dynamic_pointer_cast(obj);
if (dict) {
diff --git a/lib/base/math-script.cpp b/lib/base/math-script.cpp
index dab48a5df..288cbca12 100644
--- a/lib/base/math-script.cpp
+++ b/lib/base/math-script.cpp
@@ -22,6 +22,7 @@
#include "base/functionwrapper.hpp"
#include "base/scriptframe.hpp"
#include "base/initialize.hpp"
+#include "base/namespace.hpp"
#include
#include
@@ -158,40 +159,44 @@ static double MathSign(double x)
}
INITIALIZE_ONCE([]() {
- Dictionary::Ptr mathObj = new Dictionary({
- /* Constants */
- { "E", 2.71828182845904523536 },
- { "LN2", 0.693147180559945309417 },
- { "LN10", 2.30258509299404568402 },
- { "LOG2E", 1.44269504088896340736 },
- { "LOG10E", 0.434294481903251827651 },
- { "PI", 3.14159265358979323846 },
- { "SQRT1_2", 0.707106781186547524401 },
- { "SQRT2", 1.41421356237309504880 },
+ auto mathNSBehavior = new ConstNamespaceBehavior();
+ Namespace::Ptr mathNS = new Namespace(mathNSBehavior);
- /* Methods */
- { "abs", new Function("Math#abs", MathAbs, { "x" }, true) },
- { "acos", new Function("Math#acos", MathAcos, { "x" }, true) },
- { "asin", new Function("Math#asin", MathAsin, { "x" }, true) },
- { "atan", new Function("Math#atan", MathAtan, { "x" }, true) },
- { "atan2", new Function("Math#atan2", MathAtan2, { "x", "y" }, true) },
- { "ceil", new Function("Math#ceil", MathCeil, { "x" }, true) },
- { "cos", new Function("Math#cos", MathCos, { "x" }, true) },
- { "exp", new Function("Math#exp", MathExp, { "x" }, true) },
- { "floor", new Function("Math#floor", MathFloor, { "x" }, true) },
- { "log", new Function("Math#log", MathLog, { "x" }, true) },
- { "max", new Function("Math#max", MathMax, {}, true) },
- { "min", new Function("Math#min", MathMin, {}, true) },
- { "pow", new Function("Math#pow", MathPow, { "x", "y" }, true) },
- { "random", new Function("Math#random", MathRandom, {}, true) },
- { "round", new Function("Math#round", MathRound, { "x" }, true) },
- { "sin", new Function("Math#sin", MathSin, { "x" }, true) },
- { "sqrt", new Function("Math#sqrt", MathSqrt, { "x" }, true) },
- { "tan", new Function("Math#tan", MathTan, { "x" }, true) },
- { "isnan", new Function("Math#isnan", MathIsnan, { "x" }, true) },
- { "isinf", new Function("Math#isinf", MathIsinf, { "x" }, true) },
- { "sign", new Function("Math#sign", MathSign, { "x" }, true) }
- });
+ /* Constants */
+ mathNS->Set("E", 2.71828182845904523536);
+ mathNS->Set("LN2", 0.693147180559945309417);
+ mathNS->Set("LN10", 2.30258509299404568402);
+ mathNS->Set("LOG2E", 1.44269504088896340736);
+ mathNS->Set("LOG10E", 0.434294481903251827651);
+ mathNS->Set("PI", 3.14159265358979323846);
+ mathNS->Set("SQRT1_2", 0.707106781186547524401);
+ mathNS->Set("SQRT2", 1.41421356237309504880);
- ScriptGlobal::Set("Math", mathObj);
+ /* Methods */
+ mathNS->Set("abs", new Function("Math#abs", MathAbs, { "x" }, true));
+ mathNS->Set("acos", new Function("Math#acos", MathAcos, { "x" }, true));
+ mathNS->Set("asin", new Function("Math#asin", MathAsin, { "x" }, true));
+ mathNS->Set("atan", new Function("Math#atan", MathAtan, { "x" }, true));
+ mathNS->Set("atan2", new Function("Math#atan2", MathAtan2, { "x", "y" }, true));
+ mathNS->Set("ceil", new Function("Math#ceil", MathCeil, { "x" }, true));
+ mathNS->Set("cos", new Function("Math#cos", MathCos, { "x" }, true));
+ mathNS->Set("exp", new Function("Math#exp", MathExp, { "x" }, true));
+ mathNS->Set("floor", new Function("Math#floor", MathFloor, { "x" }, true));
+ mathNS->Set("log", new Function("Math#log", MathLog, { "x" }, true));
+ mathNS->Set("max", new Function("Math#max", MathMax, {}, true));
+ mathNS->Set("min", new Function("Math#min", MathMin, {}, true));
+ mathNS->Set("pow", new Function("Math#pow", MathPow, { "x", "y" }, true));
+ mathNS->Set("random", new Function("Math#random", MathRandom, {}, true));
+ mathNS->Set("round", new Function("Math#round", MathRound, { "x" }, true));
+ mathNS->Set("sin", new Function("Math#sin", MathSin, { "x" }, true));
+ mathNS->Set("sqrt", new Function("Math#sqrt", MathSqrt, { "x" }, true));
+ mathNS->Set("tan", new Function("Math#tan", MathTan, { "x" }, true));
+ mathNS->Set("isnan", new Function("Math#isnan", MathIsnan, { "x" }, true));
+ mathNS->Set("isinf", new Function("Math#isinf", MathIsinf, { "x" }, true));
+ mathNS->Set("sign", new Function("Math#sign", MathSign, { "x" }, true));
+
+ mathNSBehavior->Freeze();
+
+ Namespace::Ptr systemNS = ScriptGlobal::Get("System");
+ systemNS->SetAttribute("Math", std::make_shared(mathNS));
});
diff --git a/lib/base/namespace-script.cpp b/lib/base/namespace-script.cpp
new file mode 100644
index 000000000..9f5471d60
--- /dev/null
+++ b/lib/base/namespace-script.cpp
@@ -0,0 +1,101 @@
+/******************************************************************************
+ * Icinga 2 *
+ * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
+ * *
+ * 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/namespace.hpp"
+#include "base/function.hpp"
+#include "base/functionwrapper.hpp"
+#include "base/scriptframe.hpp"
+#include "base/array.hpp"
+
+using namespace icinga;
+
+static void NamespaceSet(const String& key, const Value& value)
+{
+ ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+ Namespace::Ptr self = static_cast(vframe->Self);
+ REQUIRE_NOT_NULL(self);
+ self->Set(key, value);
+}
+
+static Value NamespaceGet(const String& key)
+{
+ ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+ Namespace::Ptr self = static_cast(vframe->Self);
+ REQUIRE_NOT_NULL(self);
+ return self->Get(key);
+}
+
+static void NamespaceRemove(const String& key)
+{
+ ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+ Namespace::Ptr self = static_cast(vframe->Self);
+ REQUIRE_NOT_NULL(self);
+ self->Remove(key);
+}
+
+static bool NamespaceContains(const String& key)
+{
+ ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+ Namespace::Ptr self = static_cast(vframe->Self);
+ REQUIRE_NOT_NULL(self);
+ return self->Contains(key);
+}
+
+static Array::Ptr NamespaceKeys()
+{
+ ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+ Namespace::Ptr self = static_cast(vframe->Self);
+ REQUIRE_NOT_NULL(self);
+
+ ArrayData keys;
+ ObjectLock olock(self);
+ for (const Namespace::Pair& kv : self) {
+ keys.push_back(kv.first);
+ }
+ return new Array(std::move(keys));
+}
+
+static Array::Ptr NamespaceValues()
+{
+ ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
+ Namespace::Ptr self = static_cast(vframe->Self);
+ REQUIRE_NOT_NULL(self);
+
+ ArrayData values;
+ ObjectLock olock(self);
+ for (const Namespace::Pair& kv : self) {
+ values.push_back(kv.second->Get());
+ }
+ return new Array(std::move(values));
+}
+
+Object::Ptr Namespace::GetPrototype()
+{
+ static Dictionary::Ptr prototype = new Dictionary({
+ { "set", new Function("Namespace#set", NamespaceSet, { "key", "value" }) },
+ { "get", new Function("Namespace#get", NamespaceGet, { "key" }) },
+ { "remove", new Function("Namespace#remove", NamespaceRemove, { "key" }) },
+ { "contains", new Function("Namespace#contains", NamespaceContains, { "key" }, true) },
+ { "keys", new Function("Namespace#keys", NamespaceKeys, {}, true) },
+ { "values", new Function("Namespace#values", NamespaceValues, {}, true) },
+ });
+
+ return prototype;
+}
+
diff --git a/lib/base/namespace.cpp b/lib/base/namespace.cpp
new file mode 100644
index 000000000..d106a0ee3
--- /dev/null
+++ b/lib/base/namespace.cpp
@@ -0,0 +1,223 @@
+/******************************************************************************
+ * Icinga 2 *
+ * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
+ * *
+ * 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/namespace.hpp"
+#include "base/objectlock.hpp"
+#include "base/debug.hpp"
+#include "base/primitivetype.hpp"
+#include "base/debuginfo.hpp"
+#include "base/exception.hpp"
+#include
+
+using namespace icinga;
+
+template class std::map >;
+
+REGISTER_PRIMITIVE_TYPE(Namespace, Object, Namespace::GetPrototype());
+
+Namespace::Namespace(NamespaceBehavior *behavior)
+ : m_Behavior(std::unique_ptr(behavior))
+{ }
+
+Value Namespace::Get(const String& field) const
+{
+ Value value;
+ if (!GetOwnField(field, &value))
+ BOOST_THROW_EXCEPTION(ScriptError("Namespace does not contain field '" + field + "'"));
+ return value;
+}
+
+bool Namespace::Get(const String& field, Value *value) const
+{
+ auto nsVal = GetAttribute(field);
+
+ if (!nsVal)
+ return false;
+
+ *value = nsVal->Get(DebugInfo());
+ return true;
+}
+
+void Namespace::Set(const String& field, const Value& value, bool overrideFrozen)
+{
+ return SetFieldByName(field, value, overrideFrozen, DebugInfo());
+}
+
+bool Namespace::Contains(const String& field) const
+{
+ return HasOwnField(field);
+}
+
+void Namespace::Remove(const String& field, bool overrideFrozen)
+{
+ m_Behavior->Remove(this, field, overrideFrozen);
+}
+
+void Namespace::RemoveAttribute(const String& field)
+{
+ ObjectLock olock(this);
+
+ Namespace::Iterator it;
+ it = m_Data.find(field);
+
+ if (it == m_Data.end())
+ return;
+
+ m_Data.erase(it);
+}
+
+std::shared_ptr Namespace::GetAttribute(const String& key) const
+{
+ ObjectLock olock(this);
+
+ auto it = m_Data.find(key);
+
+ if (it == m_Data.end())
+ return nullptr;
+
+ return it->second;
+}
+
+void Namespace::SetAttribute(const String& key, const std::shared_ptr& nsVal)
+{
+ ObjectLock olock(this);
+
+ m_Data[key] = nsVal;
+}
+
+Value Namespace::GetFieldByName(const String& field, bool, const DebugInfo& debugInfo) const
+{
+ auto nsVal = GetAttribute(field);
+
+ if (nsVal)
+ return nsVal->Get(debugInfo);
+ else
+ return GetPrototypeField(const_cast(this), field, true, debugInfo);
+}
+
+void Namespace::SetFieldByName(const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo)
+{
+ auto nsVal = GetAttribute(field);
+
+ if (!nsVal)
+ m_Behavior->Register(this, field, value, overrideFrozen, debugInfo);
+ else
+ nsVal->Set(value, overrideFrozen, debugInfo);
+}
+
+bool Namespace::HasOwnField(const String& field) const
+{
+ return GetAttribute(field) != nullptr;
+}
+
+bool Namespace::GetOwnField(const String& field, Value *result) const
+{
+ auto nsVal = GetAttribute(field);
+
+ if (!nsVal)
+ return false;
+
+ *result = nsVal->Get(DebugInfo());
+ return true;
+}
+
+EmbeddedNamespaceValue::EmbeddedNamespaceValue(const Value& value)
+ : m_Value(value)
+{ }
+
+Value EmbeddedNamespaceValue::Get(const DebugInfo& debugInfo) const
+{
+ return m_Value;
+}
+
+void EmbeddedNamespaceValue::Set(const Value& value, bool, const DebugInfo&)
+{
+ m_Value = value;
+}
+
+void ConstEmbeddedNamespaceValue::Set(const Value& value, bool overrideFrozen, const DebugInfo& debugInfo)
+{
+ if (!overrideFrozen)
+ BOOST_THROW_EXCEPTION(ScriptError("Constant must not be modified.", debugInfo));
+
+ EmbeddedNamespaceValue::Set(value, overrideFrozen, debugInfo);
+}
+
+void NamespaceBehavior::Register(const Namespace::Ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const
+{
+ ns->SetAttribute(field, std::make_shared(value));
+}
+
+void NamespaceBehavior::Remove(const Namespace::Ptr& ns, const String& field, bool overrideFrozen)
+{
+ if (!overrideFrozen) {
+ auto attr = ns->GetAttribute(field);
+
+ if (dynamic_pointer_cast(attr))
+ BOOST_THROW_EXCEPTION(ScriptError("Constants must not be removed."));
+ }
+
+ ns->RemoveAttribute(field);
+}
+
+void ConstNamespaceBehavior::Register(const Namespace::Ptr& ns, const String& field, const Value& value, bool overrideFrozen, const DebugInfo& debugInfo) const
+{
+ if (m_Frozen && !overrideFrozen)
+ BOOST_THROW_EXCEPTION(ScriptError("Namespace is read-only and must not be modified.", debugInfo));
+
+ ns->SetAttribute(field, std::make_shared(value));
+}
+
+void ConstNamespaceBehavior::Remove(const Namespace::Ptr& ns, const String& field, bool overrideFrozen)
+{
+ if (m_Frozen && !overrideFrozen)
+ BOOST_THROW_EXCEPTION(ScriptError("Namespace is read-only and must not be modified."));
+
+ NamespaceBehavior::Remove(ns, field, overrideFrozen);
+}
+
+void ConstNamespaceBehavior::Freeze()
+{
+ m_Frozen = true;
+}
+
+Namespace::Iterator Namespace::Begin()
+{
+ ASSERT(OwnsLock());
+
+ return m_Data.begin();
+}
+
+Namespace::Iterator Namespace::End()
+{
+ ASSERT(OwnsLock());
+
+ return m_Data.end();
+}
+
+Namespace::Iterator icinga::begin(const Namespace::Ptr& x)
+{
+ return x->Begin();
+}
+
+Namespace::Iterator icinga::end(const Namespace::Ptr& x)
+{
+ return x->End();
+}
+
diff --git a/lib/base/namespace.hpp b/lib/base/namespace.hpp
new file mode 100644
index 000000000..21ebe929b
--- /dev/null
+++ b/lib/base/namespace.hpp
@@ -0,0 +1,123 @@
+/******************************************************************************
+ * Icinga 2 *
+ * Copyright (C) 2012-2018 Icinga Development Team (https://www.icinga.com/) *
+ * *
+ * 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 NAMESPACE_H
+#define NAMESPACE_H
+
+#include "base/i2-base.hpp"
+#include "base/object.hpp"
+#include "base/value.hpp"
+#include "base/debuginfo.hpp"
+#include