Example #1
0
def test_set_and_strip_class_attributes() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("add classA")

    shell.onecmd("set classA length size_t")
    shell.onecmd("set classA isValid bool")
    shell.onecmd("set classA")
    shell.onecmd("set classA a")
    shell.onecmd("set classA a b c d")
    shell.onecmd("set classA 'badname' value")

    assert shell._UMLShell__diagram.get_class_attributes("classA") == {
        "length": "size_t",
        "isValid": "bool",
    }

    shell.onecmd("strip classA length")
    shell.onecmd("strip classA isValid")
    shell.onecmd("strip classA")
    shell.onecmd("strip classA a")
    shell.onecmd("strip classA a b c d")
    shell.onecmd("strip classA 'badname'")

    assert shell._UMLShell__diagram.get_class_attributes("classA") == {}

    shell.onecmd("strip classA fakeAttr")
    shell.onecmd("set fakeClass length size_t")
    shell.onecmd("strip fakeClass fakeAttr")
Example #2
0
def test_set_and_strip_relationship_attribute() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("add class1")
    shell.onecmd("add class2")
    shell.onecmd("add class3")
    shell.onecmd("add [class1,class2]")
    shell.onecmd("add [class2,class3]")
    shell.onecmd("set [class1,class2] category aggregate")
    shell.onecmd("set [class2,class3] category aggregate")

    assert shell._UMLShell__diagram.get_relationship_attributes("class1", "class2") == {
        "category": "aggregate"
    }

    assert shell._UMLShell__diagram.get_relationship_attributes("class2", "class3") == {
        "category": "aggregate"
    }

    shell.onecmd("strip [class2,class3] category")
    shell.onecmd("strip [class1,class2] category")

    assert (
        shell._UMLShell__diagram.get_relationship_attributes("class2", "class3") == {}
    )
    assert (
        shell._UMLShell__diagram.get_relationship_attributes("class1", "class2") == {}
    )
Example #3
0
def test_complete() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("add lcassC")
    shell.onecmd("add abc")
    shell.onecmd("add classA")
    shell.onecmd("add classB")
    shell.onecmd("add class12")

    assert shell.complete_remove("clas", "remove clas", 7, 11) == [
        "classA",
        "classB",
        "class12",
    ]
    assert shell.complete_remove("lcas", "remove clas", 7, 11) == ["lcassC"]
    assert shell.complete_remove("nart", "remove clas", 7, 11) == []

    assert shell.complete_rename("clas", "rename clas", 7, 11) == [
        "classA",
        "classB",
        "class12",
    ]
    assert shell.complete_rename("lcas", "rename clas", 7, 11) == ["lcassC"]
    assert shell.complete_rename("nart", "rename clas", 7, 11) == []
Example #4
0
def test_rename() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("add classA")
    shell.onecmd("add classA")
    shell.onecmd("add classB")
    shell.onecmd("rename classA classC")

    assert sorted(shell._UMLShell__diagram.get_all_class_names()) == [
        "classB",
        "classC",
    ]

    shell.onecmd("rename classB")

    assert sorted(shell._UMLShell__diagram.get_all_class_names()) == [
        "classB",
        "classC",
    ]

    shell.onecmd("remove classB")
    shell.onecmd("remove classC")
    shell.onecmd("remove classC")
    shell.onecmd("add classA")
    shell.onecmd("rename classA [classB]")
    shell.onecmd("rename classZOINKS classB")
    shell.onecmd("rename classA classA")

    assert shell._UMLShell__diagram.get_all_class_names() == ["classA"]
Example #5
0
def test_remove_relationship() -> None:
    umld: UMLDiagram = UMLDiagram()

    # Create initial classes
    umld.add_class("Alpha")
    umld.add_class("Beta")
    umld.add_class("Gamma")

    # Create initial relationships
    umld.add_relationship("Alpha", "Beta")
    umld.add_relationship("Alpha", "Gamma")

    # Double-removal prevention test
    assert umld.remove_relationship("Alpha", "Beta")
    assert not umld.remove_relationship("Alpha", "Beta")
    assert not umld.remove_relationship("Beta", "Alpha")

    # Class order reversal test
    assert umld.remove_relationship("Gamma", "Alpha")
    assert not umld.remove_relationship("Gamma", "Alpha")
    assert not umld.remove_relationship("Alpha", "Gamma")

    # Non-existent relationship check test
    assert not umld.remove_relationship("Gamma", "Gamma")

    # Non-existent class check test
    assert not umld.remove_relationship("FakeClass", "Alpha")
    assert not umld.remove_relationship("Alpha", "FakeClass")
    assert not umld.remove_relationship("FakeClass", "FakeClass")
Example #6
0
def test_save_and_load(tmp_path: Path):
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()
    file_path: str = str(tmp_path / "saveandloadtest.yaml")

    shell.onecmd("save")
    shell.onecmd(
        "save '\\乃工厶 匚卄凵𠘨厶凵丂!//#$!(*"
        "*)'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    )
    shell.onecmd("load")
    shell.onecmd(
        "load '\\乃工厶 匚卄凵𠘨厶凵丂!//#$!(*"
        "*)'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    )

    shell.onecmd("add classToLoad")
    assert shell._UMLShell__diagram.get_all_class_names() == ["classToLoad"]

    shell.onecmd("save " + file_path)

    scruml.uml_context_cli.input = lambda s: "n"
    shell.onecmd("load " + file_path)

    shell.onecmd("remove classToLoad")
    assert shell._UMLShell__diagram.get_all_class_names() == []

    shell.onecmd("load " + file_path)
    assert shell._UMLShell__diagram.get_all_class_names() == ["classToLoad"]
Example #7
0
def test_rename() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("add classA")
    shell.onecmd("add classA")
    shell.onecmd("add classB")
    shell.onecmd("rename classA classC")

    assert sorted(shell._UMLShell__diagram.get_all_class_names()) == [
        "classB",
        "classC",
    ]

    shell.onecmd("rename classB")

    assert sorted(shell._UMLShell__diagram.get_all_class_names()) == [
        "classB",
        "classC",
    ]

    shell.onecmd("remove classB")
    shell.onecmd("remove classC")
    shell.onecmd("remove classC")
    shell.onecmd("add classA")
    shell.onecmd("rename classA [classB]")
    shell.onecmd("rename classZOINKS classB")
    shell.onecmd("rename classA classA")

    assert shell._UMLShell__diagram.get_all_class_names() == ["classA"]

    # TODO: Update this when renaming is fully implemented, should probably be removed
    shell._UMLShell__rename_class("Not implemented")
    shell._UMLShell__rename_relationship("Not implemented")
Example #8
0
def test_add_and_remove_relationships() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("add classA")
    shell.onecmd("add classB")
    shell.onecmd("add classC")

    assert shell._UMLShell__diagram.get_all_relationship_pairs() == []

    assert shell._UMLShell__diagram.get_all_relationship_pairs() == []

    shell.onecmd("add [classA,classB]")
    shell.onecmd("add [classA,classB]")

    assert shell._UMLShell__diagram.get_all_relationship_pairs() == [
        ("classA", "classB")
    ]

    shell.onecmd("remove [classC,classB]")
    shell.onecmd("remove [classA,classC]")

    assert shell._UMLShell__diagram.get_all_relationship_pairs() == [
        ("classA", "classB")
    ]

    shell.onecmd("remove [classA,classB]")

    assert shell._UMLShell__diagram.get_all_relationship_pairs() == []

    shell.onecmd("add [classA,fakeClass]")
    shell.onecmd("remove [fakeClass,classB]")

    assert shell._UMLShell__diagram.get_all_relationship_pairs() == []
def test_save_and_load(tmp_path: Path) -> None:
    diagram: UMLDiagram = UMLDiagram()
    file_path: str = str(tmp_path / "saveandloadtest.yaml")
    assert diagram.add_class("ClassToLoad")
    assert uml_filesystem_io.save_diagram(diagram, file_path)
    diagram = uml_filesystem_io.load_diagram(file_path)
    assert "ClassToLoad" in diagram.get_all_class_names()
Example #10
0
def test_add_and_remove_classes() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("add classA")
    shell.onecmd("add classB")
    shell.onecmd("add [invalidclass]")
    shell.onecmd("add invalid class")
    shell.onecmd("add 'invalidclass'")
    shell.onecmd('add "invalidclass"')
    shell.onecmd("add [classA,classB]")
    shell.onecmd("add [[invalidrelationship],invalid class]")
    shell.onecmd("add [invalid class,[invalidrelationship]]")

    assert not shell._UMLShell__diagram.add_class("classA")
    assert not shell._UMLShell__diagram.add_class("classB")
    assert sorted(shell._UMLShell__diagram.get_all_class_names()) == [
        "classA",
        "classB",
    ]

    shell.onecmd("remove classA")
    shell.onecmd("remove classB")
    shell.onecmd("remove [invalidclass]")
    shell.onecmd("remove invalid class")
    shell.onecmd("remove [classA,classB]")
    shell.onecmd("remove [[invalidrelationship],invalid class]")
    shell.onecmd("remove [invalid class,[invalidrelationship]]")
    shell.onecmd("remove [ classA, classB ]")

    assert shell._UMLShell__diagram.get_all_class_names() == []
Example #11
0
def test_help() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("")
    shell.onecmd("help")
    shell.onecmd("help identifiers")
    shell.onecmd("help parameters")
Example #12
0
def test_exit() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    scruml.uml_context_cli.input = lambda s: "n"
    shell.onecmd("exit")
    scruml.uml_context_cli.input = lambda s: "y"
    shell.onecmd("exit")
Example #13
0
def test_yes_no_prompt() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    scruml.uml_context_cli.input = lambda s: "y"
    shell._UMLShell__yes_or_no_prompt("Test prompt 1")
    scruml.uml_context_cli.input = lambda s: "n"
    shell._UMLShell__yes_or_no_prompt("Test prompt 2")
def test_save(tmp_path: Path) -> None:
    diagram: UMLDiagram = UMLDiagram()
    file_path: str = str(tmp_path / "savetest.yaml")
    assert diagram.add_class("SavedClassName")
    assert uml_filesystem_io.save_diagram(diagram, file_path)
    invalid_file_path: str = str(
        tmp_path /
        '\\乃工厶 匚卄凵𠘨厶凵丂!//#$!(*"🕳️🚶"*)Ỏ̷͖͈̞̩͎̻̫̫̜͉̠̫͕̭̭̫̫̹̗̹͈̼̠̖͍͚̥͈̮̼͕̠̤̯̻̥̬̗̼̳̤̳̬̪̹͚̞̼̠͕̼̠̦͚̫͔̯̹͉͉̘͎͕̼̣̝͙̱̟̹̩̟̳̦̭͉̮̖̭̣̣̞̙̗̜̺̭̻̥͚͙̝̦̲̱͉͖͉̰̦͎̫̣̼͎͍̠̮͓̹̹͉̤̰̗̙͕͇͔̱͕̭͈̳̗̭͔̘̖̺̮̜̠͖̘͓̳͕̟̠̱̫̤͓͔̘̰̲͙͍͇̙͎̣̼̗̖͙̯͉̠̟͈͍͕̪͓̝̩̦̖̹̼̠̘̮͚̟͉̺̜͍͓̯̳'
    )
    assert not uml_filesystem_io.save_diagram(diagram, invalid_file_path)
Example #15
0
def test_print() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("print")
    shell.onecmd("add classA")
    shell.onecmd("add classB")
    shell.onecmd("print")
    shell.onecmd("add [classA,classB]")
    shell.onecmd("print")
Example #16
0
def test_remove_class_attribute() -> None:
    umld: UMLDiagram = UMLDiagram()

    umld.add_class("ClassA")
    umld.add_class("ClassB")
    umld.set_class_attribute("ClassA", "attr1", "type1")
    assert umld.remove_class_attribute("ClassA", "attr1")
    assert not umld.remove_class_attribute("ClassA", "attr1")
    assert not umld.remove_class_attribute("ClassB", "lenth")
    umld.set_class_attribute("ClassB", "length", "size_t")
    assert umld.remove_class_attribute("ClassB", "length")
    umld.set_class_attribute("ClassB", "name", "string")
    assert not umld.remove_class_attribute("fakeClass", "name")
Example #17
0
def test_rename_class() -> None:
    umld: UMLDiagram = UMLDiagram()

    assert not umld.rename_class("ClassA", "ClassB")
    umld.add_class("ClassA")
    assert umld.rename_class("ClassA", "ClassB")
    # "ClassA" should no longer exist in the diagram
    assert not umld.rename_class("ClassA", "ClassB")
    assert umld.add_class("ClassA")
    assert sorted(umld.get_all_class_names())
    assert not umld.rename_class("ClassA", "ClassB")
    assert not umld.rename_class("ClassB", "ClassA")
    assert not umld.rename_class("NewClass", "ClassB")
Example #18
0
def test_add_class() -> None:
    umld: UMLDiagram = UMLDiagram()

    assert umld.add_class("ClassA")
    assert not umld.add_class("ClassA")
    assert not umld.add_class("ClassA")

    assert umld.add_class("a")
    assert not umld.add_class("a")

    assert umld.add_class("1234")
    assert not umld.add_class("1234")

    assert umld.add_class("None")
    assert not umld.add_class("None")
Example #19
0
def test_do_variable() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("add classA")

    shell.onecmd("variable set classA")
    shell.onecmd("variable set 'classsA' myint -v public -t int")
    shell.onecmd("variable set classA 'myvar' -v public -t int")
    shell.onecmd("variable set classA myvar -s ahhh -t int")
    shell.onecmd("variable set classA myvar -v 'public' -t int")
    shell.onecmd("variable set classA myvar -v public -t int")

    shell.onecmd("variable remove classA myvar")

    assert shell._UMLShell__diagram.get_class_attributes("classA") == {}
Example #20
0
def test_set_relationship_attribute() -> None:
    umld: UMLDiagram = UMLDiagram()

    umld.add_class("Alpha")
    umld.add_class("Beta")
    umld.add_class("Gamma")
    umld.add_relationship("Alpha", "Beta")

    assert umld.set_relationship_attribute("Alpha", "Beta", "quantifierA",
                                           "many")

    assert not umld.set_relationship_attribute("FakeClass", "Beta", "category",
                                               "aggregation")
    assert not umld.set_relationship_attribute("Alpha", "FakeClass",
                                               "category", "aggregation")
    assert not umld.set_relationship_attribute("Alpha", "Gamma", "quantifierA",
                                               "many")
Example #21
0
def test_set_class_attribute() -> None:
    umld: UMLDiagram = UMLDiagram()

    umld.add_class("ClassA")
    umld.add_class("ClassB")
    # Add valid attributes
    assert umld.set_class_attribute("ClassA", "attr1", "type1")
    assert umld.set_class_attribute("ClassA", "attr2", "type2")
    assert umld.set_class_attribute("ClassA", "xPos", "int")
    assert umld.set_class_attribute("ClassB", "attr1", "type1")
    # Edit valid attributes
    assert umld.set_class_attribute("ClassA", "attr1", "newType1")
    assert umld.set_class_attribute("ClassA", "attr1", "newType1")
    assert umld.set_class_attribute("ClassA", "attr2", "newType2")
    assert umld.set_class_attribute("ClassA", "xPos", "xPos")
    assert umld.set_class_attribute("ClassB", "attr1", "newType1")
    # Invalid input
    assert not umld.set_class_attribute("fakeClass", "attr1", "val1")
Example #22
0
def test_get_relationship_attributes() -> None:
    umld: UMLDiagram = UMLDiagram()
    umld.add_class("Alpha")
    umld.add_class("Beta")
    umld.add_class("Gamma")
    umld.add_relationship("Alpha", "Beta")

    assert not umld.get_relationship_attributes("Alpha", "Beta")

    umld.set_relationship_attribute("Alpha", "Beta", "category", "aggregation")
    assert umld.get_relationship_attributes("Alpha", "Beta") == {
        "category": "aggregation"
    }

    umld.remove_relationship_attribute("Alpha", "Beta", "category")
    assert not umld.get_relationship_attributes("Alpha", "Beta")

    umld.set_relationship_attribute("Alpha", "Beta", "quantifierA", "many")
    umld.set_relationship_attribute("Alpha", "Beta", "quantifierB", "one")
    umld.set_relationship_attribute("Alpha", "Beta", "category",
                                    "generalization")
    assert umld.get_relationship_attributes("Alpha", "Beta") == {
        "quantifierA": "many",
        "quantifierB": "one",
        "category": "generalization",
    }

    umld.remove_relationship_attribute("Alpha", "Beta", "quantifierB")
    assert umld.get_relationship_attributes("Alpha", "Beta") == {
        "quantifierA": "many",
        "category": "generalization",
    }
    umld.remove_relationship_attribute("Alpha", "Beta", "quantifierA")
    assert umld.get_relationship_attributes("Alpha", "Beta") == {
        "category": "generalization"
    }
    umld.remove_relationship_attribute("Alpha", "Beta", "category")
    assert not umld.get_relationship_attributes("Alpha", "Beta")

    assert not umld.get_relationship_attributes("FakeClass", "Beta")
    assert not umld.get_relationship_attributes("Alpha", "FakeClass")
    assert not umld.get_relationship_attributes("Alpha", "Beta")
    assert not umld.get_relationship_attributes("Alpha", "Gamma")
Example #23
0
def test_get_all_class_names() -> None:
    umld: UMLDiagram = UMLDiagram()

    assert umld.get_all_class_names() == []
    umld.add_class("ClassA")
    assert umld.get_all_class_names() == ["ClassA"]
    umld.add_class("a")

    # order not guaranteed, sort for the comparison
    assert sorted(umld.get_all_class_names()) == ["ClassA", "a"]
    umld.add_class("1234")
    assert sorted(umld.get_all_class_names()) == ["1234", "ClassA", "a"]
    umld.remove_class("ClassA")
    assert sorted(umld.get_all_class_names()) == ["1234", "a"]
    umld.remove_class("1234")

    assert umld.get_all_class_names() == ["a"]
    umld.remove_class("a")
    assert umld.get_all_class_names() == []
Example #24
0
def test_add_relationship() -> None:
    umld: UMLDiagram = UMLDiagram()

    # Create initial classes
    umld.add_class("Alpha")
    umld.add_class("Beta")
    umld.add_class("Gamma")

    # Duplicate relationship prevention test
    assert umld.add_relationship("Alpha", "Beta")
    assert not umld.add_relationship("Alpha", "Beta")
    assert not umld.add_relationship("Beta", "Alpha")

    # Self referential relationship prevention test
    assert not umld.add_relationship("Gamma", "Gamma")

    # Non-existent class check test
    assert not umld.add_relationship("FakeClass", "Alpha")
    assert not umld.add_relationship("Alpha", "FakeClass")
    assert not umld.add_relationship("FakeClass", "FakeClass")
Example #25
0
def test_do_function() -> None:
    shell: __UMLShell = __UMLShell()
    shell._UMLShell__diagram = UMLDiagram()

    shell.onecmd("add classA")
    shell.onecmd("function set")
    shell.onecmd("function set 'badname' myfunc")
    shell.onecmd("function set classA 'badfunc'")
    shell.onecmd("function set classB myfunc")
    shell.onecmd(
        "function set classA myfunc -v public -s int -p [int'myint,float'myfloat] garbage"
    )
    shell.onecmd(
        "function set classA myfunc -v public -s 'ddd' -p [int'myint,float'myfloat}"
    )
    shell.onecmd(
        "function set classA myfunc -v public -s int - p [int'myint,float'myfloat]"
    )

    shell.onecmd("function remove classA myfunc")

    assert shell._UMLShell__diagram.get_class_attributes("classA") == {}
Example #26
0
def test_add_and_remove_class() -> None:
    api: __API = __API()
    api._API__diagram = UMLDiagram()

    add_class_data = {"class_name": "classA", "x": 0, "y": 20}

    result = api.addClass(add_class_data)
    assert result == ""

    result2 = api.addClass(add_class_data)
    assert not result2 == ""

    add_class_data_case_2 = {"class_name": "class A", "x": 0, "y": 20}

    result3 = api.addClass(add_class_data_case_2)
    assert not result3 == ""

    try:
        api.removeClass(add_class_data["class_name"])
        assert False
    except Exception as e:
        assert True
Example #27
0
def test_get_class_attributes() -> None:
    umld: UMLDiagram = UMLDiagram()

    assert not umld.get_class_attributes("fakeClass")
    umld.add_class("ClassA")
    assert not umld.get_class_attributes("ClassA")
    umld.set_class_attribute("ClassA", "name", "string")
    assert umld.get_class_attributes("ClassA")
    umld.set_class_attribute("ClassA", "length", "size_t")
    # Note: dictionary equality comparison does not consider the order of entries
    assert umld.get_class_attributes("ClassA")
    umld.set_class_attribute("ClassA", "isValid", "bool")
    assert umld.get_class_attributes("ClassA") == {
        "name": "string",
        "length": "size_t",
        "isValid": "bool",
    }
    umld.remove_class_attribute("ClassA", "length")
    assert umld.get_class_attributes("ClassA")
    umld.remove_class_attribute("ClassA", "name")
    assert umld.get_class_attributes("ClassA")
    umld.remove_class_attribute("ClassA", "isValid")
    assert not umld.get_class_attributes("ClassA")
class __UMLShell(cmd.Cmd):
    """Simple CLI context for interacting with ScrUML."""

    # --------------------
    # Static variables

    intro: str = "Welcome to ScrUML.\nType in 'help' to receive a list of possible commands."
    doc_header: str = "Commands (type 'help <command>'):"
    misc_header: str = "Other topics (type 'help <topic>'):"
    prompt: str = "ScrUML> "
    __diagram: UMLDiagram = UMLDiagram()

    # TODO: Standardize print strings, refactor dispatch functions

    def __yes_or_no_prompt(self, question: str) -> bool:
        """Prompts the user with "question" and waits for a "y/n" answer."""
        while "Waiting for valid reply":
            reply = str(input(question + " [y/n]: ")).lower().strip()
            if reply[:1] == "y":
                return True
            if reply[:1] == "n":
                return False
        assert (
            False
        ), "This is unreachable code. If you are reading this, you're in trouble, buddy."

    # --------------------
    # "Help" commands

    # ----------
    # emptyline

    def emptyline(self) -> bool:
        """Outputs a help line when the user does not enter a command."""
        print("Please enter a command.")
        print("Type in 'help' to receive a list of possible commands.")
        return False

    # ----------
    # help_identifiers

    def help_identifiers(self) -> None:
        """Prints helpful information about identifier formatting."""
        print(
            "Identifiers are the names that represent elements within your UML diagram.\n"
        )
        print("Valid identifier types: Classes, Relationships\n")
        print("Classes:")
        print(
            "  Class identifiers consist of a single string with no whitespace or quotes."
        )
        print("  Class identifiers cannot start and end with an opening and")
        print("  closing bracket.")
        print("Examples:")
        print('  Valid: "MyClass", "--lispObject!", "class20-ab"')
        print('  Invalid: "[someclass]", "my class", "class\'""\n')
        print("Relationships:")
        print("  Relationship identifiers consist of a bracketed list of")
        print("  2-3 valid class identifiers. The first two class identifiers")
        print(
            "  represent the two classes that the relationship exists between."
        )
        print(
            "  The third identifier represents the name of the relationship,")
        print("  though it is optional-- relationships can be unnamed.")
        print("Examples:")
        print('  Valid: "[myclassA,myclassB]", "[--object1,--object1,copy]"')
        print('  Invalid: "[class, my class]", "[[someclass], ]"')

    # ----------
    # help_parameters

    def help_parameters(self) -> None:
        """Prints helpful information about parameter list formatting."""
        print(
            "Parameter lists can be used to specify what arguments can be provided to a member function.\n"
        )
        print("Parameter lists:")
        print(
            "  Parameter lists consist of a bracketed list of one or more valid parameters."
        )
        print(
            "  Each parameter consists of a valid type name followed by a valid parameter name,"
        )
        print(
            "  separated by a single quote. Type names and parameter names cannot start and end with an"
        )
        print(
            "  opening and closing bracket, and contain no whitespace or quotes."
        )
        print("Examples:")
        print(
            "  Valid: \"[float'a,int'b,MyType'cx-var]\", \"[var'myStr,var'someVar]\""
        )
        print(
            '  Invalid: "[ float;myvarone int myvartwo]", "[myVar, myVarB,]"')

    # ----------
    # "Add" command

    # ----------
    # do_add

    def do_add(self, arg: str) -> None:
        """Usage: add <identifier>
Adds new class or relationship if one with that identifier does not already exist
For help with identifiers, type in 'help identifiers'"""

        # Check the number of arguments
        args: List[str] = arg.split()
        if len(args) != 1:
            print(
                "Please provide a valid class or relationship identifier as an argument."
            )
            print(self.do_add.__doc__)
            return

        # Grab arguments
        identifier: str = args[0]

        # Classify what kind of identifier was provided
        identifier_class: Optional[str] = uml_utilities.classify_identifier(
            identifier)

        # Handle class identifiers
        if identifier_class == "class":
            class_id: Optional[str] = uml_utilities.parse_class_identifier(
                identifier)
            if class_id is not None:
                self.__add_class(arg)
                return

        # Handle relationship identifiers
        elif identifier_class == "relationship":
            rel_id: Optional[Tuple[
                str,
                str]] = uml_utilities.parse_relationship_identifier(identifier)
            if rel_id is not None:
                self.__add_relationship(rel_id)
                return

        # If we don't return before we get here, the user provided a bad argument
        print("Invalid argument provided.")
        print(self.do_add.__doc__)

    # ----------
    # __add_class

    def __add_class(self, class_id: str) -> None:
        """Adds new class if one with that name does not already exist"""
        if not self.__diagram.add_class(class_id):
            print("Class '{}' already exists in the diagram".format(class_id))
        else:
            print("Added class '{}'".format(class_id))

    # ----------
    # __add_relationship

    def __add_relationship(self, rel_id: Tuple[str, str]) -> None:
        """Adds new relationship if one with that identifier does not already exist"""

        # Check whether both classes exist
        class_list: List[str] = self.__diagram.get_all_class_names()
        if rel_id[0] not in class_list:
            print("Class '{}' does not exist in the diagram".format(rel_id[0]))
            return
        if rel_id[1] not in class_list:
            print("Class '{}' does not exist in the diagram".format(rel_id[1]))
            return

        # Check if both args are the same class, no class can have a relationship with itself
        if rel_id[0] == rel_id[1]:
            print("A class cannot have a relationship with itself.")
            return

        # Add the relationship to the diagram, checking for an error
        if not self.__diagram.add_relationship(rel_id[0], rel_id[1]):
            print("Relationship '{}' already exists in the diagram".format(
                uml_utilities.stringify_relationship_identifier(
                    rel_id[0], rel_id[1])))
        else:
            print("Added relationship '{}'".format(
                uml_utilities.stringify_relationship_identifier(
                    rel_id[0], rel_id[1])))

    # --------------------
    # "Remove" command

    # ----------
    # do_remove

    def do_remove(self, arg: str) -> None:
        """Usage: remove <identifier>
Removes a class or relationship if one with that identifier exists in the diagram
For help with identifiers, type in 'help identifiers'"""

        # Check the number of arguments
        args: List[str] = arg.split()
        if len(args) != 1:
            print(
                "Please provide a valid class or relationship identifier as an argument."
            )
            print(self.do_remove.__doc__)
            return

        # Grab arguments
        identifier: str = args[0]

        # Classify what kind of identifier was provided
        identifier_class: Optional[str] = uml_utilities.classify_identifier(
            identifier)

        # Handle class identifiers
        if identifier_class == "class":
            class_id: Optional[str] = uml_utilities.parse_class_identifier(
                identifier)
            if class_id is not None:
                self.__remove_class(arg)
                return

        # Handle relationship identifiers
        elif identifier_class == "relationship":
            rel_id: Optional[Tuple[
                str,
                str]] = uml_utilities.parse_relationship_identifier(identifier)
            if rel_id is not None:
                self.__remove_relationship(rel_id)
                return

        # If we don't return before we get here, the user provided a bad argument
        print("Invalid argument provided.")
        print(self.do_remove.__doc__)

    # ----------
    # complete_remove

    def complete_remove(self, text: str, line: str, begidx: str,
                        endidx: str) -> List[str]:
        """Return potential completions for the "remove" command"""
        # TODO: Relationship completions
        return [
            name for name in self.__diagram.get_all_class_names()
            if name.startswith(text)
        ]

    # ----------
    # __remove_class

    def __remove_class(self, class_id: str) -> None:
        """Removes class if it exists"""
        if not self.__diagram.remove_class(class_id):
            print("Class '{}' does not exist in the diagram".format(class_id))
        else:
            print("Removed class '{}'".format(class_id))

    # ----------
    # __remove_relationship

    def __remove_relationship(self, rel_id: Tuple[str, str]) -> None:
        """Removes relationship if one with that identifier exists"""

        # Check whether both classes exist
        class_list: List[str] = self.__diagram.get_all_class_names()
        if rel_id[0] not in class_list:
            print("Class '{}' does not exist in the diagram".format(rel_id[0]))
            return
        if rel_id[1] not in class_list:
            print("Class '{}' does not exist in the diagram".format(rel_id[1]))
            return

        # Remove the relationship from the diagram, checking for an error
        if not self.__diagram.remove_relationship(rel_id[0], rel_id[1]):
            print("Relationship '{}' does not exist in the diagram".format(
                uml_utilities.stringify_relationship_identifier(
                    rel_id[0], rel_id[1])))
        else:
            print("Relationship '{}' has been removed from the diagram".format(
                uml_utilities.stringify_relationship_identifier(
                    rel_id[0], rel_id[1])))

    # --------------------
    # "Function" command

    # ----------
    # do_function

    def do_function(self, arg: str) -> None:
        """Usage: function [set|remove] <class name> <function name> [-v VISIBILITY] [-t TYPE] [-p PARAMETERS]
Adds, modifies, or removes a member function for the specified class
For help with formatting parameter lists, type in 'help parameters'"""

        # Check the number of arguments
        args: List[str] = arg.split()
        if len(args) < 3:
            print("Please provide a valid number of arguments.")
            print(self.do_function.__doc__)
            return

        # Grab arguments
        subcommand: str = args[0]
        class_name: str = args[1]
        func_name: str = args[2]

        # Parse the class ID and function name
        class_id: Optional[str] = uml_utilities.parse_class_identifier(
            class_name)
        func_id: Optional[str] = uml_utilities.parse_class_identifier(
            func_name)

        # Make sure that the class ID is valid
        if class_id is None:
            print(
                "Please provide a valid class name (no whitespace, quotes, or surrounding brackets)."
            )
            return

        # Make sure that the function ID is valid
        if func_id is None:
            print(
                "Please provide a valid function name (no whitespace, quotes, or surrounding brackets)."
            )
            return

        # Make sure that the class name is in the diagram
        if class_id not in self.__diagram.get_all_class_names():
            print("Class '{}' does not exist in the diagram".format(class_id))
            return

        # Handle set subcommand
        if subcommand == "set":

            # Set up argument parser for optional flags
            arg_parser: ArgumentParser = ArgumentParser(add_help=False,
                                                        usage="")
            arg_parser.add_argument("-v", "--visibility")
            arg_parser.add_argument("-t", "--type")
            arg_parser.add_argument("-p", "--parameters")
            try:
                extra_args: Namespace = arg_parser.parse_args(
                    args[3:len(args)])
            except:
                print("Invalid argument provided.")
                print(self.do_function.__doc__)
                return

            # Grab and verify any optional flag values
            func_visibility: Optional[str] = ""
            func_type: Optional[str] = ""
            param_list: Optional[List[str]] = []
            if extra_args.visibility:
                func_visibility = uml_utilities.parse_class_identifier(
                    extra_args.visibility)
            if extra_args.type:
                func_type = uml_utilities.parse_class_identifier(
                    extra_args.type)
            if extra_args.parameters:
                param_list = uml_utilities.parse_param_list(
                    extra_args.parameters)
            if func_visibility is None or func_type is None or param_list is None:
                print(
                    "Please ensure all values provided are valid (no whitespace, quotes, or surrounding brackets)."
                )
                return

            # Dispatch
            self.__function_set(class_id, func_id, func_visibility, func_type,
                                param_list)
            return

        # Handle remove subcommand
        if subcommand == "remove":
            self.__function_remove(class_id, func_id)
            return

        # If we don't return before we get here, the user provided a bad argument
        print("Invalid argument provided.")
        print(self.do_function.__doc__)

    # ----------
    # __function_set

    def __function_set(
        self,
        class_name: str,
        func_name: str,
        func_visibility: str,
        func_type: str,
        param_list: List[str],
    ) -> None:
        serialized_func: Tuple[str, str] = uml_utilities.serialize_function(
            func_visibility, func_type, func_name, param_list)
        self.__set_class_attribute(class_name, serialized_func[0],
                                   serialized_func[1])

    # ----------
    # __function_remove

    def __function_remove(self, class_name: str, func_name: str) -> None:
        self.__strip_class_attribute(class_name, f"[F:{func_name}]")

    # --------------------
    # "Variable" command

    # ----------
    # do_variable

    def do_variable(self, arg: str) -> None:
        """Usage: variable [set|remove] <class name> <variable name> [-v VISIBILITY] [-t TYPE]
Adds, modifies, or removes a member variable for the specified class"""

        # Check the number of arguments
        args: List[str] = arg.split()
        if len(args) < 3:
            print("Please provide a valid number of arguments.")
            print(self.do_variable.__doc__)
            return

        # Grab arguments
        subcommand: str = args[0]
        class_name: str = args[1]
        var_name: str = args[2]

        # Parse the class ID and variable name
        class_id: Optional[str] = uml_utilities.parse_class_identifier(
            class_name)
        var_id: Optional[str] = uml_utilities.parse_class_identifier(var_name)

        # Make sure that the class ID is valid
        if class_id is None:
            print(
                "Please provide a valid class name (no whitespace, quotes, or surrounding brackets)."
            )
            return

        # Make sure that the variable ID is valid
        if var_id is None:
            print(
                "Please provide a valid variable name (no whitespace, quotes, or surrounding brackets)."
            )
            return

        # Make sure that the class name is in the diagram
        if class_id not in self.__diagram.get_all_class_names():
            print("Class '{}' does not exist in the diagram".format(class_id))
            return

        # Handle set subcommand
        if subcommand == "set":

            # Set up argument parser for optional flags
            arg_parser: ArgumentParser = ArgumentParser(add_help=False,
                                                        usage="")
            arg_parser.add_argument("-v", "--visibility")
            arg_parser.add_argument("-t", "--type")
            try:
                extra_args: Namespace = arg_parser.parse_args(
                    args[3:len(args)])
            except:
                print("Invalid argument provided.")
                print(self.do_function.__doc__)
                return

            # Grab and verify any optional flag values
            var_visibility: Optional[str] = ""
            var_type: Optional[str] = ""
            if extra_args.visibility:
                var_visibility = uml_utilities.parse_class_identifier(
                    extra_args.visibility)
            if extra_args.type:
                var_type = uml_utilities.parse_class_identifier(
                    extra_args.type)
            if var_visibility is None or var_type is None:
                print(
                    "Please ensure all values provided are valid (no whitespace, quotes, or surrounding brackets)."
                )
                return

            # Dispatch
            self.__variable_set(class_id, var_id, var_visibility, var_type)
            return

        # Handle remove subcommand
        if subcommand == "remove":
            self.__variable_remove(class_id, var_id)
            return

        # If we don't return before we get here, the user provided a bad argument
        print("Invalid argument provided.")
        print(self.do_variable.__doc__)

    # ----------
    # __variable_set

    def __variable_set(self, class_name: str, var_name: str,
                       var_visibility: str, var_type: str) -> None:
        serialized_var: Tuple[str, str] = uml_utilities.serialize_variable(
            var_visibility, var_type, var_name)
        self.__set_class_attribute(class_name, serialized_var[0],
                                   serialized_var[1])

    # ----------
    # __variable_remove

    def __variable_remove(self, class_name: str, var_name: str) -> None:
        self.__strip_class_attribute(class_name, f"[V:{var_name}]")

    # --------------------
    # "Set" command

    # ----------
    # do_set

    def do_set(self, arg: str) -> None:
        """Usage: set <identifier> <attribute_name> <attribute_value>
Adds or modifies the attribute for the specified class
For help with identifiers, type in 'help identifiers'"""

        # Check the number of arguments
        args: List[str] = arg.split()
        if len(args) != 3:
            print("Please provide a valid number of arguments.")
            print(self.do_set.__doc__)
            return

        # Grab arguments
        identifier: str = args[0]
        attr_name: str = args[1]
        attr_value: str = args[2]

        # Classify what kind of identifier was provided
        identifier_class: Optional[str] = uml_utilities.classify_identifier(
            identifier)

        # Ensure attribute name is valid
        if not uml_utilities.parse_class_identifier(attr_name):
            print(
                "Please provide a valid attribute name (no whitespace, quotes, or surrounding brackets)."
            )
            return

        # Handle class identifiers
        if identifier_class == "class":
            class_id: Optional[str] = uml_utilities.parse_class_identifier(
                identifier)
            if class_id is not None:
                self.__set_class_attribute(class_id, attr_name, attr_value)
                return

        # Handle relationship identifiers
        elif identifier_class == "relationship":
            rel_id: Optional[Tuple[
                str,
                str]] = uml_utilities.parse_relationship_identifier(identifier)
            if rel_id is not None:
                self.__set_relationship_attribute(rel_id, attr_name,
                                                  attr_value)
                return

        # If we don't return before we get here, the user provided a bad argument
        print("Invalid argument provided.")
        print(self.do_set.__doc__)

    # ----------
    # __set_class_attribute

    def __set_class_attribute(self, class_id: str, attr_name: str,
                              attr_value: str) -> None:
        """Adds or modifies the attribute with attr_name for the specified class"""
        if not self.__diagram.set_class_attribute(class_id, attr_name,
                                                  attr_value):
            print("Class '{}' does not exist in the diagram".format(class_id))
        else:
            print("Set attribute '{}' with value '{}' in class '{}'".format(
                attr_name, attr_value, class_id))

    # ----------
    # __set_relationship_attribute

    def __set_relationship_attribute(self, rel_id: Tuple[str, str],
                                     attr_name: str, attr_value: str) -> None:
        """Adds or modifies the attribute with attr_name for the specified relationship."""

        # Check whether both classes exist
        class_list: List[str] = self.__diagram.get_all_class_names()
        if rel_id[0] not in class_list:
            print("Class '{}' does not exist in the diagram".format(rel_id[0]))
            return
        if rel_id[1] not in class_list:
            print("Class '{}' does not exist in the diagram".format(rel_id[1]))
            return

        # Set the relationship's attribute, checking for an error
        if not self.__diagram.set_relationship_attribute(
                rel_id[0], rel_id[1], attr_name, attr_value):
            print("Relationship {} does not exist in the diagram".format(
                uml_utilities.stringify_relationship_identifier(
                    rel_id[0], rel_id[1])))
        else:
            print("Set attribute '{}' with value '{}' in relationship '{}'".
                  format(
                      attr_name,
                      attr_value,
                      uml_utilities.stringify_relationship_identifier(
                          rel_id[0], rel_id[1]),
                  ))

    # --------------------
    # "Strip" command

    # ----------
    # do_strip

    def do_strip(self, arg: str) -> None:
        """Usage strip <identifier> <attribute_name>
Removes the attribute for the specified class"""

        # Check the number of arguments
        args: List[str] = arg.split()
        if len(args) != 2:
            print("Please provide a valid number of arguments.")
            print(self.do_strip.__doc__)
            return

        # Grab arguments
        identifier: str = args[0]
        attr_name: str = args[1]

        # Classify what kind of identifier was provided
        identifier_class: Optional[str] = uml_utilities.classify_identifier(
            identifier)

        # Ensure attribute name is valid
        if not uml_utilities.parse_class_identifier(attr_name):
            print(
                "Please provide a valid attribute name (no whitespace, quotes, or surrounding brackets)."
            )
            return

        # Handle class identifiers
        if identifier_class == "class":
            class_id: Optional[str] = uml_utilities.parse_class_identifier(
                identifier)
            if class_id is not None:
                self.__strip_class_attribute(class_id, attr_name)
                return

        # Handle relationship identifiers
        elif identifier_class == "relationship":
            rel_id: Optional[Tuple[
                str,
                str]] = uml_utilities.parse_relationship_identifier(identifier)
            if rel_id is not None:
                self.__strip_relationship_attribute(rel_id, attr_name)
                return

        # If we don't return before we get here, the user provided a bad argument
        print("Invalid argument provided.")
        print(self.do_strip.__doc__)

    # ----------
    # __strip_class_attribute

    def __strip_class_attribute(self, class_id: str, attr_name: str) -> None:
        """Removes the attribute for the specified class"""
        if class_id not in self.__diagram.get_all_class_names():
            print("Class '{}' does not exist in the diagram".format(class_id))
            return
        if not self.__diagram.remove_class_attribute(class_id, attr_name):
            print(
                "Class '{}' does not have an attribute with name '{}'".format(
                    class_id, attr_name))
        else:
            print("Removed attribute '{}' from class '{}'".format(
                attr_name, class_id))

    # ----------
    # __strip_relationship_attribute

    def __strip_relationship_attribute(self, rel_id: Tuple[str, str],
                                       attr_name: str) -> None:
        """Removes the attribute with attr_name for the specified relationship."""

        # Check whether both classes exist
        class_list: List[str] = self.__diagram.get_all_class_names()
        if rel_id[0] not in class_list:
            print("Class '{}' does not exist in the diagram".format(rel_id[0]))
            return
        if rel_id[1] not in class_list:
            print("Class '{}' does not exist in the diagram".format(rel_id[1]))
            return

        # Remove the relationship's attribute, checking for an error
        if not self.__diagram.remove_relationship_attribute(
                rel_id[0], rel_id[1], attr_name):
            print(
                "Relationship '{}' does not have an attribute with name '{}'".
                format(
                    uml_utilities.stringify_relationship_identifier(
                        rel_id[0], rel_id[1]),
                    attr_name,
                ))
        else:
            print("Removed attribute '{}' from relationship '{}'".format(
                attr_name,
                uml_utilities.stringify_relationship_identifier(
                    rel_id[0], rel_id[1]),
            ))

    # --------------------
    # "Rename" command

    # ----------
    # do_rename

    def do_rename(self, arg: str) -> None:
        """Usage: rename <class name> <new class name>
Changes the name of a class if it exists and the new name is not taken
For help with identifiers, type in 'help identifiers"""

        # Check the number of arguments
        args: List[str] = arg.split()
        if len(args) != 2:
            print("Please provide a valid number of arguments.")
            print(self.do_rename.__doc__)
            return

        # Grab arguments
        old_class_name: str = args[0]
        new_class_name: str = args[1]

        # Parse the class IDs
        class_ids: List[Optional[str]] = [
            uml_utilities.parse_class_identifier(old_class_name),
            uml_utilities.parse_class_identifier(new_class_name),
        ]

        # Make sure that the class ids are valid
        if not class_ids[0] or not class_ids[1]:
            print("Please provide two valid class class_ids as arguments.")
            print(self.do_rename.__doc__)
            return

        # Make sure that the old class name is in the diagram
        if class_ids[0] not in self.__diagram.get_all_class_names():
            print("Class '{}' does not exist in the diagram".format(
                class_ids[0]))
            return

        # Rename the class, checking for an error
        if not self.__diagram.rename_class(str(class_ids[0]), str(
                class_ids[1])):
            print("Class with name '{}' already exists in the diagram".format(
                class_ids[1]))
            return
        else:
            print("Renamed class '{}' to '{}'".format(class_ids[0],
                                                      class_ids[1]))

        # If we don't return before we get here, the user provided a bad argument
        print("Invalid argument provided.")
        print(self.do_rename.__doc__)

    # ----------
    # complete_rename

    def complete_rename(self, text: str, line: str, begidx: str,
                        endidx: str) -> List[str]:
        """Return potential completions for the "rename" command"""
        # TODO: Split arguments
        return [
            name for name in self.__diagram.get_all_class_names()
            if name.startswith(text)
        ]

    # --------------------
    # Other functions

    # ----------
    # do_print

    def do_print(self, arg: str) -> None:
        """Usage: print
Prints all elements present in the current diagram"""

        class_names: List[str] = self.__diagram.get_all_class_names()

        if not class_names:
            print("The current diagram is empty.")
            return

        print("All classes in the current diagram:")

        for class_name in class_names:

            print(" " + class_name + ":")

            attributes: Optional[Dict[
                str, str]] = self.__diagram.get_class_attributes(class_name)

            if attributes is None:
                raise Exception(
                    "Fatal: Attributes entry for class '{}' not found.".format(
                        class_name))

            if attributes == {}:
                print("   No attributes")

            for attribute_name, attribute_value in attributes.items():
                print("   {} = {}".format(attribute_name, attribute_value))

        relationship_pairs: List[Tuple[
            str, str]] = self.__diagram.get_all_relationship_pairs()

        print("All relationships in the current diagram:")

        if not relationship_pairs:
            print(" No relationships")

        for relationship_pair in relationship_pairs:

            relationship: Optional[Dict[
                str, str]] = self.__diagram.get_relationship_between(
                    relationship_pair[0], relationship_pair[1])

            if relationship is None:
                raise Exception(
                    "Fatal: No relationship entry for class pair '[{},{}]'".
                    format(relationship_pair[0], relationship_pair[1]))

            print(" {} <-> {}:".format(relationship_pair[0],
                                       relationship_pair[1]))

            relationship_attributes: Optional[Dict[
                str, str]] = self.__diagram.get_relationship_attributes(
                    relationship_pair[0], relationship_pair[1])
            if relationship_attributes is None or relationship_attributes == {}:
                print("   No attributes")
            else:
                for (
                        rel_attribute_name,
                        rel_attribute_value,
                ) in relationship_attributes.items():
                    print("   {} = {}".format(rel_attribute_name,
                                              rel_attribute_value))

    # ----------
    # do_save

    def do_save(self, arg: str) -> None:
        """Usage: save <file name>
Saves the current UML diagram to a file"""
        if arg.isspace() or not arg:
            print("Please provide a file name.")
            print(self.do_save.__doc__)
            return
        if uml_filesystem_io.save_diagram(self.__diagram, arg):
            print("Diagram successfully saved to '{}'".format(arg))
        else:
            print("Failed to save diagram to '{}'".format(arg))

    # ----------
    # do_load

    def do_load(self, arg: str) -> None:
        """Usage: load <file name>
Loads an existing UML diagram from a file"""
        if arg.isspace() or not arg:
            print("Please provide a file name.")
            print(self.do_load.__doc__)
            return
        if not os.path.isfile(arg):
            print("No file found at '{}'".format(arg))
            return
        if self.__diagram.get_all_class_names():
            print("Loading a diagram will overwrite the current diagram.")
            if not self.__yes_or_no_prompt("Continue?"):
                return
        print("Loading diagram from '{}'".format(arg))
        self.__diagram = uml_filesystem_io.load_diagram(arg)

    # ----------
    # do_exit

    def do_exit(self, arg: str) -> bool:
        """Usage: exit
Exits ScrUML"""
        if self.__yes_or_no_prompt("Really exit ScrUML?"):
            print("Thank you for using ScrUML. Goodbye!")
            return True
        return False
 def newDiagramFile(self, params: str) -> None:
     """Creates a new, blank diagram."""
     self.__diagram = UMLDiagram()
Example #30
0
def load_diagram(file_path: str) -> UMLDiagram:
    with open(file_path, "r") as diagram_file:
        diagram: UMLDiagram = UMLDiagram()
        if diagram_file:
            diagram = yaml.full_load(diagram_file)
        return diagram