/** * src/class_diagram/model/diagram.cc * * Copyright (c) 2021-2022 Bartek Kryza * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "diagram.h" #include "util/error.h" #include "util/util.h" #include namespace clanguml::class_diagram::model { const std::vector> & diagram::classes() const { return classes_; } const std::vector> &diagram::enums() const { return enums_; } common::model::diagram_t diagram::type() const { return common::model::diagram_t::kClass; } std::optional< std::reference_wrapper> diagram::get(const std::string &full_name) const { // type_safe::optional_ref // res; std::optional< std::reference_wrapper> res; res = get_class(full_name); if (res.has_value()) return res; res = get_enum(full_name); return res; } bool diagram::has_class(const class_ &c) const { return std::any_of(classes_.cbegin(), classes_.cend(), [&c](const auto &cc) { return cc.get() == c; }); } bool diagram::has_enum(const enum_ &e) const { return std::any_of(enums_.cbegin(), enums_.cend(), [&e](const auto &ee) { return ee.get().full_name() == e.full_name(); }); } std::optional> diagram::get_class( const std::string &name) const { for (const auto &c : classes_) { if (c.get().full_name(false) == name) { return {c}; } } return {}; } std::optional> diagram::get_enum( const std::string &name) const { for (const auto &e : enums_) { if (e.get().full_name(false) == name) { return {e}; } } return {}; } void diagram::add_type_alias(std::unique_ptr &&ta) { LOG_DBG( "Adding global alias: {} -> {}", ta->alias(), ta->underlying_type()); type_aliases_[ta->alias()] = std::move(ta); } bool diagram::add_package(std::unique_ptr &&p) { LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true)); auto ns = p->get_relative_namespace(); return add_element(ns, std::move(p)); } bool diagram::add_class(std::unique_ptr &&c) { const auto base_name = c->name(); const auto full_name = c->full_name(false); LOG_DBG("Adding class: {}::{}, {}", c->get_namespace().to_string(), base_name, full_name); if (util::contains(base_name, "::")) throw std::runtime_error("Name cannot contain namespace: " + base_name); if (util::contains(base_name, "<")) throw std::runtime_error("Name cannot contain <: " + base_name); if (util::contains(base_name, "*")) throw std::runtime_error("Name cannot contain *: " + base_name); const auto ns = c->get_relative_namespace(); auto name = base_name; auto name_with_ns = c->name_and_ns(); auto name_and_ns = ns | name; const auto &cc = *c; auto cc_ref = std::ref(cc); if (!has_class(cc)) { if (add_element(ns, std::move(c))) classes_.push_back(std::move(cc_ref)); const auto &el = get_element(name_and_ns).value(); if ((el.name() != name) || !(el.get_relative_namespace() == ns)) throw std::runtime_error( "Invalid element stored in the diagram tree"); return true; } LOG_DBG("Class {} ({}) already in the model", base_name, full_name); return false; } bool diagram::add_enum(std::unique_ptr &&e) { const auto full_name = e->name(); LOG_DBG("Adding enum: {}", full_name); assert(!util::contains(e->name(), "::")); auto e_ref = std::ref(*e); auto ns = e->get_relative_namespace(); if (!has_enum(*e)) { if (add_element(ns, std::move(e))) { enums_.emplace_back(std::move(e_ref)); return true; } } LOG_DBG("Enum {} already in the model", full_name); return false; } void diagram::get_parents( clanguml::common::reference_set &parents) const { bool found_new{false}; for (const auto &parent : parents) { for (const auto &pp : parent.get().parents()) { const auto p = get_class(pp.name()); if (p.has_value()) { auto [it, found] = parents.emplace(p.value()); if (found) found_new = true; } } } if (found_new) { get_parents(parents); } } bool diagram::has_element( clanguml::common::model::diagram_element::id_t id) const { for (const auto &c : classes_) { if (c.get().id() == id) return true; } for (const auto &c : enums_) { if (c.get().id() == id) return true; } return false; } std::string diagram::to_alias( clanguml::common::model::diagram_element::id_t id) const { LOG_DBG("Looking for alias for {}", id); for (const auto &c : classes_) { if (c.get().id() == id) { return c.get().alias(); } } for (const auto &e : enums_) { if (e.get().id() == id) return e.get().alias(); } throw error::uml_alias_missing(fmt::format("Missing alias for {}", id)); } } namespace clanguml::common::model { template <> bool check_diagram_type(diagram_t t) { return t == diagram_t::kClass; } }