Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add generation of string classes for cpp #42

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 120 additions & 77 deletions cimgen/languages/cpp/lang_pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ def setup(output_path: str, cgmes_profile_details: list, cim_namespace: str): #
{"filename": "cpp_enum_header_template.mustache", "ext": ".hpp"},
{"filename": "cpp_enum_object_template.mustache", "ext": ".cpp"},
]
string_template_files = [
{"filename": "cpp_string_header_template.mustache", "ext": ".hpp"},
{"filename": "cpp_string_object_template.mustache", "ext": ".cpp"},
]


def get_class_location(class_name, class_map, version): # NOSONAR
Expand All @@ -60,10 +64,12 @@ def run_template(output_path, class_details):
templates = float_template_files
elif class_details["is_an_enum_class"]:
templates = enum_template_files
elif class_details["is_a_primitive_class"]:
templates = string_template_files
else:
templates = template_files

if class_details["class_name"] in ("String", "Integer", "Boolean", "Date"):
if class_details["class_name"] in ("Integer", "Boolean"):
# These classes are defined already
# We have to implement operators for them
return
Expand All @@ -75,7 +81,6 @@ def run_template(output_path, class_details):

def _write_templated_file(class_file, class_details, template_filename):
with open(class_file, "w", encoding="utf-8") as file:
class_details["setDefault"] = _set_default
templates = files("cimgen.languages.cpp.templates")
with templates.joinpath(template_filename).open(encoding="utf-8") as f:
args = {
Expand Down Expand Up @@ -109,7 +114,7 @@ def insert_assign_fn(text, render):
label = attribute_json["label"]
class_name = attribute_json["domain"]
return (
'assign_map.insert(std::make_pair(std::string("cim:'
' assign_map.insert(std::make_pair(std::string("cim:'
+ class_name
+ "."
+ label
Expand All @@ -129,7 +134,7 @@ def insert_class_assign_fn(text, render):
label = attribute_json["label"]
class_name = attribute_json["domain"]
return (
'assign_map.insert(std::make_pair(std::string("cim:'
' assign_map.insert(std::make_pair(std::string("cim:'
+ class_name
+ "."
+ label
Expand All @@ -147,13 +152,13 @@ def create_nullptr_assigns(text, render):
return ""
else:
attributes_json = eval(attributes_txt)
nullptr_init_string = ": "
nullptr_init_string = ""
for attribute in attributes_json:
if attribute["is_class_attribute"]:
nullptr_init_string += "LABEL(nullptr), ".replace("LABEL", attribute["label"])

if len(nullptr_init_string) > 2:
return nullptr_init_string[:-2]
return " : " + nullptr_init_string[:-2]
else:
return ""

Expand All @@ -168,34 +173,72 @@ def create_class_assign(text, render):
if _attribute_is_primitive_or_datatype_or_enum(attribute_json):
return ""
if attribute_json["is_list_attribute"]:
assign = (
"""
bool assign_OBJECT_CLASS_LABEL(BaseClass* BaseClass_ptr1, BaseClass* BaseClass_ptr2) {
if(OBJECT_CLASS* element = dynamic_cast<OBJECT_CLASS*>(BaseClass_ptr1)) {
if(dynamic_cast<ATTRIBUTE_CLASS*>(BaseClass_ptr2) != nullptr) {
element->LABEL.push_back(dynamic_cast<ATTRIBUTE_CLASS*>(BaseClass_ptr2));
if "inverseRole" in attribute_json:
inverse = attribute_json["inverseRole"].split(".")
assign = (
"""
bool assign_INVERSEC_INVERSEL(BaseClass*, BaseClass*);
bool assign_OBJECT_CLASS_LABEL(BaseClass* BaseClass_ptr1, BaseClass* BaseClass_ptr2)
{
OBJECT_CLASS* element = dynamic_cast<OBJECT_CLASS*>(BaseClass_ptr1);
ATTRIBUTE_CLASS* element2 = dynamic_cast<ATTRIBUTE_CLASS*>(BaseClass_ptr2);
if (element != nullptr && element2 != nullptr)
{
if (std::find(element->LABEL.begin(), element->LABEL.end(), element2) == element->LABEL.end())
{
element->LABEL.push_back(element2);
return assign_INVERSEC_INVERSEL(BaseClass_ptr2, BaseClass_ptr1);
}
return true;
}
return false;
}""".replace( # noqa: E101,W191
"OBJECT_CLASS", attribute_json["domain"]
)
.replace("ATTRIBUTE_CLASS", attribute_class)
.replace("LABEL", attribute_json["label"])
.replace("INVERSEC", inverse[0])
.replace("INVERSEL", inverse[1])
)
else:
assign = (
"""
bool assign_OBJECT_CLASS_LABEL(BaseClass* BaseClass_ptr1, BaseClass* BaseClass_ptr2)
{
if (OBJECT_CLASS* element = dynamic_cast<OBJECT_CLASS*>(BaseClass_ptr1))
{
if (dynamic_cast<ATTRIBUTE_CLASS*>(BaseClass_ptr2) != nullptr)
{
element->LABEL.push_back(dynamic_cast<ATTRIBUTE_CLASS*>(BaseClass_ptr2));
return true;
}
}
return false;
}""".replace( # noqa: E101,W191
"OBJECT_CLASS", attribute_json["domain"]
"OBJECT_CLASS", attribute_json["domain"]
)
.replace("ATTRIBUTE_CLASS", attribute_class)
.replace("LABEL", attribute_json["label"])
)
.replace("ATTRIBUTE_CLASS", attribute_class)
.replace("LABEL", attribute_json["label"])
)
elif "inverseRole" in attribute_json and attribute_json["is_used"]:
elif "inverseRole" in attribute_json:
inverse = attribute_json["inverseRole"].split(".")
assign = (
"""
bool assign_INVERSEC_INVERSEL(BaseClass*, BaseClass*);
bool assign_OBJECT_CLASS_LABEL(BaseClass* BaseClass_ptr1, BaseClass* BaseClass_ptr2) {
if(OBJECT_CLASS* element = dynamic_cast<OBJECT_CLASS*>(BaseClass_ptr1)) {
element->LABEL = dynamic_cast<ATTRIBUTE_CLASS*>(BaseClass_ptr2);
if(element->LABEL != nullptr)
return assign_INVERSEC_INVERSEL(BaseClass_ptr2, BaseClass_ptr1);
}
return false;
bool assign_OBJECT_CLASS_LABEL(BaseClass* BaseClass_ptr1, BaseClass* BaseClass_ptr2)
{
OBJECT_CLASS* element = dynamic_cast<OBJECT_CLASS*>(BaseClass_ptr1);
ATTRIBUTE_CLASS* element2 = dynamic_cast<ATTRIBUTE_CLASS*>(BaseClass_ptr2);
if (element != nullptr && element2 != nullptr)
{
if (element->LABEL != element2)
{
element->LABEL = element2;
return assign_INVERSEC_INVERSEL(BaseClass_ptr2, BaseClass_ptr1);
}
return true;
}
return false;
}""".replace( # noqa: E101,W191
"OBJECT_CLASS", attribute_json["domain"]
)
Expand All @@ -207,13 +250,17 @@ def create_class_assign(text, render):
else:
assign = (
"""
bool assign_OBJECT_CLASS_LABEL(BaseClass* BaseClass_ptr1, BaseClass* BaseClass_ptr2) {
if(OBJECT_CLASS* element = dynamic_cast<OBJECT_CLASS*>(BaseClass_ptr1)) {
element->LABEL = dynamic_cast<ATTRIBUTE_CLASS*>(BaseClass_ptr2);
if(element->LABEL != nullptr)
return true;
}
return false;
bool assign_OBJECT_CLASS_LABEL(BaseClass* BaseClass_ptr1, BaseClass* BaseClass_ptr2)
{
if(OBJECT_CLASS* element = dynamic_cast<OBJECT_CLASS*>(BaseClass_ptr1))
{
element->LABEL = dynamic_cast<ATTRIBUTE_CLASS*>(BaseClass_ptr2);
if (element->LABEL != nullptr)
{
return true;
}
}
return false;
}""".replace( # noqa: E101,W191
"OBJECT_CLASS", attribute_json["domain"]
)
Expand All @@ -228,48 +275,31 @@ def create_assign(text, render):
attribute_txt = render(text)
attribute_json = eval(attribute_txt)
assign = ""
_class = attribute_json["attribute_class"]
if not _attribute_is_primitive_or_datatype_or_enum(attribute_json):
return ""
label_without_keyword = attribute_json["label"]
if label_without_keyword == "switch":
label_without_keyword = "_switch"

if _class != "String":
assign = (
"""
bool assign_CLASS_LABEL(std::stringstream &buffer, BaseClass* BaseClass_ptr1) {
if(CLASS* element = dynamic_cast<CLASS*>(BaseClass_ptr1)) {
buffer >> element->LBL_WO_KEYWORD;
if(buffer.fail())
return false;
else
return true;
}
else
return false;
}""".replace( # noqa: E101,W191
"CLASS", attribute_json["domain"]
)
.replace("LABEL", attribute_json["label"])
.replace("LBL_WO_KEYWORD", label_without_keyword)
)
else:
assign = """
bool assign_CLASS_LABEL(std::stringstream &buffer, BaseClass* BaseClass_ptr1) {
if(CLASS* element = dynamic_cast<CLASS*>(BaseClass_ptr1)) {
element->LABEL = buffer.str();
if(buffer.fail())
assign = (
"""
bool assign_CLASS_LABEL(std::stringstream &buffer, BaseClass* BaseClass_ptr1)
{
if (CLASS* element = dynamic_cast<CLASS*>(BaseClass_ptr1))
{
buffer >> element->LBL_WO_KEYWORD;
if (buffer.fail())
return false;
else
return true;
}
return false;
}""".replace( # noqa: E101,W191
"CLASS", attribute_json["domain"]
).replace(
"LABEL", attribute_json["label"]
)
.replace("LABEL", attribute_json["label"])
.replace("LBL_WO_KEYWORD", label_without_keyword)
)

return assign

Expand Down Expand Up @@ -301,8 +331,8 @@ def _create_attribute_includes(text, render):
for attribute in attributes:
if _attribute_is_primitive_or_datatype_or_enum(attribute):
unique[attribute["attribute_class"]] = True
for clarse in unique:
include_string += '\n#include "' + clarse + '.hpp"'
for clarse in sorted(unique):
include_string += '#include "' + clarse + '.hpp"\n'
return include_string


Expand All @@ -317,8 +347,8 @@ def _create_attribute_class_declarations(text, render):
for attribute in attributes:
if attribute["is_class_attribute"] or attribute["is_list_attribute"]:
unique[attribute["attribute_class"]] = True
for clarse in unique:
include_string += "\nclass " + clarse + ";"
for clarse in sorted(unique):
include_string += " class " + clarse + ";\n"
return include_string


Expand Down Expand Up @@ -372,34 +402,31 @@ def _attribute_is_primitive_or_datatype(attribute: dict) -> bool:
"Factory",
"Folders",
"IEC61970",
"String",
"Task",
"UnknownType",
]

iec61970_blacklist = ["CIMClassList", "CIMNamespaces", "Folders", "Task", "IEC61970"]


def _is_enum_class(filepath):
def _is_primitive_or_enum_class(filepath):
with open(filepath, encoding="utf-8") as f:
try:
for line in f:
if "enum class" in line:
return True
if "static const BaseClassDefiner declare();" in line:
return False
except UnicodeDecodeError as error:
print("Warning: UnicodeDecodeError parsing {0}: {1}".format(filepath, error))
return False
return True


def _create_header_include_file(directory, header_include_filename, header, footer, before, after, blacklist):

lines = []

for filename in sorted(os.listdir(directory)):
filepath = os.path.join(directory, filename)
basepath, ext = os.path.splitext(filepath)
basename = os.path.basename(basepath)
if ext == ".hpp" and not _is_enum_class(filepath) and basename not in blacklist:
if ext == ".hpp" and not _is_primitive_or_enum_class(filepath) and basename not in blacklist:
lines.append(before + basename + after)
for line in lines:
header.append(line)
Expand All @@ -414,12 +441,18 @@ def resolve_headers(path: str, version: str): # NOSONAR
class_list_header = [
"#ifndef CIMCLASSLIST_H\n",
"#define CIMCLASSLIST_H\n",
"using namespace CIMPP;\n",
"/*\n",
"Generated from the CGMES files via cimgen: https://github.com/sogno-platform/cimgen\n",
"*/\n",
"#include <list>\n",
"static std::list<BaseClassDefiner> CIMClassList = {\n",
'#include "IEC61970.hpp"\n',
"using namespace CIMPP;\n",
"static std::list<BaseClassDefiner> CIMClassList =\n",
"{\n",
]
class_list_footer = [
" UnknownType::declare() };\n",
" UnknownType::declare(),\n",
"};\n",
"#endif // CIMCLASSLIST_H\n",
]

Expand All @@ -428,13 +461,23 @@ def resolve_headers(path: str, version: str): # NOSONAR
"CIMClassList.hpp",
class_list_header,
class_list_footer,
" ",
" ",
"::declare(),\n",
class_blacklist,
)

iec61970_header = ["#ifndef IEC61970_H\n", "#define IEC61970_H\n"]
iec61970_footer = ['#include "UnknownType.hpp"\n', "#endif"]
iec61970_header = [
"#ifndef IEC61970_H\n",
"#define IEC61970_H\n",
"/*\n",
"Generated from the CGMES files via cimgen: https://github.com/sogno-platform/cimgen\n",
"*/\n",
"\n",
]
iec61970_footer = [
'#include "UnknownType.hpp"\n',
"#endif",
]

_create_header_include_file(
path,
Expand Down
20 changes: 20 additions & 0 deletions cimgen/languages/cpp/static/BaseClass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "BaseClass.hpp"

using namespace CIMPP;

BaseClass::~BaseClass() {}

const char BaseClass::debugName[] = "BaseClass";
const char* BaseClass::debugString() const
{
return BaseClass::debugName;
}

void BaseClass::addConstructToMap(std::unordered_map<std::string, BaseClass* (*)()>& factory_map) {}
void BaseClass::addPrimitiveAssignFnsToMap(std::unordered_map<std::string, assign_function>& assign_map) {}
void BaseClass::addClassAssignFnsToMap(std::unordered_map<std::string, class_assign_function>& assign_map) {}

const BaseClassDefiner BaseClass::declare()
{
return BaseClassDefiner(BaseClass::addConstructToMap, BaseClass::addPrimitiveAssignFnsToMap, BaseClass::addClassAssignFnsToMap, BaseClass::debugName);
}
1 change: 1 addition & 0 deletions cimgen/languages/cpp/static/BaseClass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "BaseClass.hpp"
Loading