def evaluate(self): # Check for the first PREXEL marker self.start_marker() # Process the first class name first_class_diagram = ClassDiagramPart() first_class_diagram.name = self.class_name() # Optionally check for FIELD and METHOD tokens self.class_body(first_class_diagram) # Optional - Check for inheritance if self.inheritance(): self.diagram.parent = first_class_diagram else: self.diagram.main = first_class_diagram # Optional - Check for aggregation but don't # consider the fields and methods following the aggregation # token as part of the aggregated class.In this position # they belong to the main class self.aggregation(include_following_tokens=False) # Process fields and methods for main class self.class_body(self.diagram.main) # Optional - Check for aggregation self.aggregation() return self.diagram
def aggregation(self, include_following_tokens=True): """ Process any aggregated classes. The class process both the AGGREGATION token and a CLASS_NAME token for the aggregated class. """ if self.current_token and self.current_token.type is Token.AGGREGATION: # Cache the current token token = self.current_token # Process AGGREGATION Token self.process_token(Token.AGGREGATION) # Confirm that a CLASS_NAME token follows the AGGREGATION TOKEN if not self.next_token_is_class_token(): self.error("There is no class name following the aggregation.") # Process aggregated ClassDiagramPart. Optionally include class body aggregated = ClassDiagramPart() aggregated.name = self.class_name() if include_following_tokens: self.class_body(aggregated) self.diagram.aggregated = aggregated # Process AggregationDiagramPart aggregation = AggregationDiagramPart() # Create a list for the fields on the main ClassDiagramPart object if not self.diagram.main.fields: self.diagram.main.fields = [] # Check that the AGGREGATION token has a name, if not # default to the aggregated class's name name = token.value["name"] if name: self.diagram.main.fields.append(name) aggregation.name = name else: # Generate an aggregation name from the aggregated class generated_name = aggregated.name.lower() self.diagram.main.fields.append(generated_name) aggregation.name = generated_name # Add multiplicity values aggregation.left_multiplicity = token.value["left_multi"] aggregation.right_multiplicity = token.value["right_multi"] # Save aggregation value to Diagram object self.diagram.aggregation = aggregation
def test_class_body(self): """ Test the class_body() method, which processes FIELD and METHOD tokens """ text = "|Airplane size color take_off()" lexer = Lexer(text) interpreter = Interpreter(lexer) interpreter.start_marker() class_diagram = ClassDiagramPart() class_diagram.name = interpreter.class_name() interpreter.class_body(class_diagram) self.assertEqual(class_diagram.fields, ["size", "color"]) self.assertEqual(class_diagram.methods, ["take_off()"])
def test_aggregation_with_missing_aggregation_name(self): """ Test the aggregation() method, """ text = "|Kitchen <>---> Cupboard" lexer = Lexer(text) interpreter = Interpreter(lexer) diagram = interpreter.diagram class_diagram_part = ClassDiagramPart() interpreter.start_marker() class_diagram_part.name = interpreter.class_name() diagram.main = class_diagram_part interpreter.aggregation() self.assertEqual(diagram.aggregation.name, "cupboard") self.assertEqual(diagram.main.fields, ["cupboard"]) self.assertEqual(diagram.aggregated.name, "Cupboard")
def test_inheritance_with_error(self): """ Test the inheritance() method, with an improper syntax """ text = "|Kitchen >>" lexer = Lexer(text) interpreter = Interpreter(lexer) class_diagram_part = ClassDiagramPart() interpreter.start_marker() class_diagram_part.name = interpreter.class_name() # Should raise a InterpreterException with self.assertRaises(InterpreterException) as context: interpreter.inheritance() self.assertEqual(context.exception.args[0], "Missing child class after \">>\"")
def test_inheritance(self): """ Test the inheritance() method, which processes an inheritance relationship """ text = "|Room height width >> Kitchen" lexer = Lexer(text) interpreter = Interpreter(lexer) diagram = interpreter.diagram class_diagram_part = ClassDiagramPart() interpreter.start_marker() class_diagram_part.name = interpreter.class_name() interpreter.class_body(class_diagram_part) if interpreter.inheritance(): diagram.parent = class_diagram_part else: diagram.main = class_diagram_part self.assertEqual(diagram.parent.name, "Room") self.assertEqual(diagram.parent.fields, ["height", "width"]) self.assertEqual(diagram.main.name, "Kitchen")
def inheritance(self): """ Process an INHERITANCE TOKEN. Sets the parent and inheritance fields on the Diagram object. ___________ ________________ | Diagram |<>-parent--->|ClassDiagramPart| |-----------| |________________| |inheritance| |parent | |___________| """ has_inheritance = False if self.current_token and self.current_token.type is Token.INHERITANCE: inheritance = InheritanceDiagramPart() # Process token self.process_token(Token.INHERITANCE) # Check CLASS_NAME token follows the INHERITANCE token if self.next_token_is_class_token(): child = ClassDiagramPart() # Determine child class name child.name = self.class_name() # Append inheritance diagram and then parent class diagram self.diagram.inheritance = inheritance self.diagram.main = child has_inheritance = True else: self.error("Missing child class after \">>\"") return has_inheritance
def test_aggregation_multi_line(self): """ Test the aggregation() method using multi-line syntax """ text = """ |Kitchen <>-cupboard--> Cupboard |size |color""" lexer = Lexer(text) interpreter = Interpreter(lexer) diagram = interpreter.diagram class_diagram_part = ClassDiagramPart() interpreter.start_marker() class_diagram_part.name = interpreter.class_name() diagram.main = class_diagram_part interpreter.aggregation(include_following_tokens=False) interpreter.class_body(diagram.main) self.assertEqual(diagram.aggregation.name, "cupboard") self.assertEqual(diagram.main.fields, ["cupboard", "size", "color"]) self.assertEqual(diagram.aggregated.name, "Cupboard")