Programming 3

University of Alicante, 2023–2024

Fifth Programming Assignment

Relative weight of this assignment in the practice grade: 15%.

Interface inheritance

Interface inheritance allows us to model systems by thinking about the responsibilities that objects must fulfil rather than how they are implemented. This allows us to build complex, scalable and loosely coupled models in a natural way.

In this assignment we will work on a word processing system that we have designed exhaustively using interface inheritance, only using implementation inheritance in situations where it is really convenient.

The library we have created allows us to represent documents that can then be exported to different formats. The current implementation allows exporting to HTML and markdown. There are hundreds of sources with information on these formats. This page and this other page (in Spanish) contain simple and comprehensive information on both formats. A a side note, all pages on the Programming 3 website are written in markdown and exported to HTML.

We provide the code for this library in the attached base project.

NOTE: Only associations rather than compositions have been considered in the design to simplify the code of this assignment and avoid having to make defensive copies.

Structure of a document

HTML and markdown documents are structured as block elements (block) or inline elements (inline). Block elements occupy the entire width of the page even if the content occupies less. When we put two block-type contents, they are stacked vertically on top of each other. Inline elements fit within the content, and usually appear next to each other on the same line. Typically these elements are embedded in a paragraph. In this web page we have surrounded the boxes of the block elements with green dashed lines, and the inline elements with red dotted lines.

What do I have to do?

Your job in this practical assignment is to add new functionalities to this library. Reading carefully the following section (Description of the design) and understanding how this library works is key to successfully implement what we ask you to do in the New functionalities section.

Description of the design

The different interfaces and classes of the library are distributed in different packages. In UML diagrams, a package is represented by a box with a top tab containing the package name. Look at each diagram to see where to find or place each interface/class.

Blocks and lines

During the assignment we will use several structures based on implementation inheritance and interface inheritance that will help us save code while allowing us to conveniently extend the application.

Based on what we have described above, we can say that a document is composed of elements of type block and elements of type inline, as shown in the following diagram. As in the end a document will be an aggregation of many elements of both types, inline (IParagraphContent) and block (IBlock), we make them both inherit from the IDocumentElement interface.

Figure 1. Elements of a document.
Figure 1. Elements of a document.

In the following sections we describe the model step by step. It is interesting to see how we can isolate parts of it without showing the whole model because with interfaces we abstract responsibilities and behaviours. This is one of the advantages of object-oriented programming.

Nested composite structures

In the system we have designed, we find ourselves in several occasions with elements of a given type that contain other elements that in turn can be of the same type. This happens with blocks. A block can contain elements of type inline but also other elements of type block, so that nested structures can be created. In the example of blocks and lines you can see how there are block elements (the ones surrounded in green), inside other blocks.

Later we will see how we have other similar composite elements such as bullet lists. The document itself is an aggregation of block and inline elements.

To avoid having to program this structure multiple times, we have created an abstract class AbstractComposite whose responsibility is to contain lists of elements IDocumentElement. In order to create nested structures we make AbstractComposite implement the IDocumentElement interface.

In this way, we can put one AbstractComposite inside another, and make structures like this:

  • AbstractComposite
    • Block
      • Inline
      • Inline
    • Block
    • AbstractComposite
      • Block
        • Inline
      • Block
        • Inline
      • AbstractComposite
        • Block
        • Inline
Figure 2. Nested compound structures
Figure 2. Nested composite structures

We can see that the addItem method is protected because it will be used only by the classes that inherit it, so we can restrict the type of elements we add.

(For more information on methods not discussed in this assignment description, refer to the source code of the library provided in the base project. You will understand the export…() methods when you read the Export section).

Document

A document (Document), by containing a composite, possibly nested, structure of blocks and lines, has been made to inherit from AbstractComposite. In a document, all elements must be inside at least one block, so we only have one add(IBlock) method. This method, in its implementation, will invoke the addItem of the base class AbstractComposite (we can do this because IBlock inherits from IDocumentElement).

Figure 3. Document as a composite element.
Figure 3. Document as a composite element.

See how with this design we could create embedded documents inside other documents because IDocument indirectly implements the IDocumentElement interface.

Paragraphs

Paragraphs (Paragraph) are blocks that contain other blocks or inline elements. This functionality is achieved by simply making Paragraph implement IBlock and inherit from AbstractComposite.

Figure 4. Paragraphs.
Figure 4. Paragraphs.

We have not put any method to add content in Paragraph. The paragraph content will be initialised with the parameters it receives in the constructor. We have two overloaded constructors, one with a list of IParagraphContent, and another, polyadic, to make it easy to instantiate simple paragraphs. Both constructors add these items to the paragraph using the addItem method inherited from AbstractComposite.

See how a paragraph can be added to a document simply by assuming that a paragraph behaves like a block, or in other words, that Paragraph implements IBlock.

Bullet lists

Bullet lists are structures like this:

  • This is an unordered bullet list element.
  • This is another element.

or like this:

  1. First element of a numbered or ordered list.
  2. Second element.

Bullet lists are block elements that contain more block elements. So we have used the same principle as for designing paragraphs, we make a bullet list behave like a block and use implementation inheritance to take advantage of the code of AbstractComposite.

Figure 4. Lists.
Figure 4. Lists.

We can see the two types of bullet lists available, ordered and unordered.

Finally, similar to Paragraph, we can just add elements in the constructor.

Headings

Headings, or section titles, are texts that behave like blocks. Depending on the hierarchy of the heading, they will have a level (1, 2, etc.) to have “Heading 1”, “Heading 2”, and so on.

As can be expected, we will find several different types of textual objects in the document. Therefore, we have created a class AbstractText whose responsibility is to encapsulate a text, and in the future, to add some additional functionality (as we will see in the tasks you have to perform later).

As the headers must behave as blocks and take advantage of the implementation of AbstractText we use the layout shown in the following diagram.

Figure 6. Headings.
Figure 6. Headings.

We can see how we could create a document with headings only by adding Heading blocks to the document.

We have included the exception EditorException that is throw if Heading is created with a level less than one.

Code blocks

Another type of text block similar to headers is the one that allows us to display source code embedded in a document. A code block is a text (AbstractText) that behaves like an IBlock and optionally has information about the programming language in which the code it contains is written.

Figure 7. Code blocks.
Figure 7. Code blocks.

Separator lines

The last type of block element we include in the library is horizontal separators in the form of a line like this one:


We have created an IMark interface to represent all blocks that have no additional content (such as text or images). The class representing these horizontal separators is HorizontalRule.

Figure 8. Separator lines.
Figure 8. Separator lines.

Images

The first and simplest of the elements of type inline is the image, as can be seen in the UML class diagram below.

Figure . Images.
Figure 9. Images.

We have left the paragraphs and the document in the diagram to show how a document could be created with images only. To do this we would create one or more images (which implement IParagraphContent), and we would construct one or more paragraphs by passing those images as parameters. Finally, we would add those paragraphs to the IDocument.

Texts

We are going to find two types of texts. Those that are inside a paragraph block and therefore must behave as inline text (IParagraphContent) and others not included in a paragraph and hat act by themselves as blocks (IBlock). The latter will be used in cases such as bullet list items (AbstractListBlock inherited by OrderedListBlock and UnorderedListBlock).

We use the ability to apply multiple interface inheritance to model this requirement on any text element through the IText interface.

Figure 10. Texts.
Figure 10. Texts.

We show in the UML the paragraphs and bullet lists to show how we could insert text into both. If we need to insert text inside a paragraph, we create a Text object and pass it to the constructor of Paragraph, which on receiving IParagraphContent admits a Text that implements that interface.

If we want to create a bullet list to which to add text, we create a Text that we will provide in the IBlock list that we send to the AbstractBlock constructor, because Text also implements IBlock.

Inline code

A special text is the InlineCode. Its behaviour and implementation are pretty much the same as Text. We have not made it inherit from it because in the future we might add functionality to Text that we don’t want InlineCode to have.

Figure 11. Inline code
Figure 11. Inline code.

Below you have the full diagram with all the classes and interfaces we have introduced.

Figura 12. Modelo.
Figura 12. Elements of a document.

Pause your reading of this assignment description. Before continuing, make sure you understand what the different types of elements in a document are and how they relate to each other.


Export

To export the document to various formats we could add a method to export each of the formats in each class (exportHTML(), exportMarkdown(),…). However, this approach has, among others, an important problem: it forces us to modify the classes for each new format.

To avoid this problem, and to allow us to extend the number of export formats without having to modify the classes of the model, we created two interfaces: IExportable and IExporter.

Each class that we want to be exportable must implement the IExportable interface. The code of the export method only delegates the responsibility of exporting to the IExporter that it receives as parameter with a code similar to this one:

class X implements IExportable {
    ····
    public void export(IExporter e) {
        e.export(this);
    }
    ····
}

In order to know how to export the X class, a String export(X) method must be added to the IExporter interface. This procedure will be repeated for each class to be exported.

To export to different formats we will only have to create concrete classes that implement IExporter.

Figure 13. IExporter and IExportable
Figure 13. IExporter and IExportable

In the class diagram above, we can see how we have made Document implement the IExportable interface so that the whole document can be exported. To force all elements in the hierarchy to implement the behaviour of being exported, we make IDocumentElement inherit the IExportable interface. This way, all the classes in the model will have at the end a export method like the one described above. In the previous diagram we have added Image and Paragraph as an example, but as you can see in all the diagrams already shown, all classes overload the export method because they indirectly get the need to implement it from IExportable.

For each format we want to export we create a class that implements IExporter. So, for this practical assignment we have created two classes, HTMLExporter and MarkdownExporter. You can see the source code in the attached Eclipse project to see how each type of element in a document is exported.

Additional behaviours

It is common to have to add more capabilities to the classes we have in the current class hierarchy without having to change them. This is especially necessary when the classes to which we want to add a new behaviour are in a library that we do not own and cannot modify.

Boldface and italics

In order to add new features to classes, such as adding hyperlinks, boldface and italics, we add a new class for each new behaviour. To add italics and boldface we created classes ItalicsTextDecorator and BoldTextDecorator respectively.

Figure 14. Text decorators.
Figure 14. Text decorators.

An AbstractDecorator is a class containing an element (decoratedElement) to which it will add an additional feature via a subclass. An AbstractTextDecorator is simply an AbstractDecorator that will behave like an IText. So, wherever we can use an IText we can replace it with any class that inherits from AbstractTextDecorator. This way, when we want to use boldface, we will use instead of Text a BoldTextDecorator that we will have built with a code like this:

IText element = new BoldTextDecorator(new Text("Texto"));

If we wanted to create a text in boldface and italics we would just surround the bold with an italic:

IText element = new ItalicsTextDecorator(new BoldTextDecorator(new Text("Texto")));

See how these new elements in the hierarchy, by indirectly implementing IExportable, force the new classes to overload the export method and IExporter to have methods to deal with them (in the diagram we have omitted the rest of the IExporter methods for ease of reading).

Tasks to perform

Up to this point, we have explained all the code of the model and the exporters that are implemented in the base project. We describe hereafter the work to be done.

New functionalities

Using as an example similar functionalities that are already designed you should extend the class hierarchy with three new classes that should inherit and/or implement whatever is necessary to pass the unit tests that are included in the base project. Note that every time you add a new class to the model you will have to add that component to the IExporter interface with the changes that this will generate in the descendant classes.

  • Currently we have a HorizontalRule class that goes down the line and draws a horizontal line. You have to create a BreakLine class that only causes a line break. In markdown that jump is simply encoded with a double \n, i.e. \n\n. In HTML, the <br> element is used. You have more information on this page. As this class can be included as an isolated block but also within a paragraph, you must additionally implement the IParagraphContent interface.
  • The system includes ways to specify boldface and italics with the BoldTextDecorator and ItalicsTextDecorator classes. You must add the StrikeThroughDecorator class for cross out text. In markdown you must surround the text to be cross out with “~~” (e.g. “~~this is cross out~~” would appear as this is cross out). In HTML, the text must be embedded in a “<del>” element (e.g. “<del>this is cross out</del>”). You have more information on this page.
  • Finally you must add the Quote class to write a quote paragraph as you can see in this page. In markdown you just prefix the content with a ‘>’ character. In HTML you include the content in a “<blockquote>” element (see this page). You can consider a quote paragraph to be a paragraph with this rich functionality by using inheritance. You must overload the two constructors inherited from Paragraph. Note that making it inherit from Paragraph is not decorating it but replacing how it is exported. To do this you must overload the export method and add the export(Quote) method in IExporter. In the HTML export you should change the <p> tag used in paragraph to <blockquote>.

Implementation of an anonymous class

In this part you must make the Image and AbstractTextContent classes implement the ITextCaseModifiable interface. It is convenient that you implement the changeCase method in AbstractTextContent and not in its subclasses to avoid repeating the same code several times.

The changeCase operation will modify the text attribute in the case of AbstractTextContent with the code this.text = modifier.changeCase(this.text);. In the case of Image, it will modify the alt attribute with the code this.alt = modifier.changeCase(this.alt);.

Figure 16. Text modifiers.
Figure 16. Text modifiers.

To implement this you will need to complete the TextsToUpperCase class so that the createTextModifier() method creates and returns an anonymous class object that implements the ITextCaseModifier interface and passes a text to uppercase (it will be checked that it is indeed anonymous and a named class is not created). You can use the method toUpperCase of the String class to perform the transformation.

Using the library

To make use of the complete class hierarchy, you must fill in the code of the es.ua.dlsi.prog3.p5.client.ExampleDocumentCreator class to return a document that generates the example_document.md and example_document.html files that are included in the unit tests so that the ExampleDocumentCreatorTest unit test does not fail.

To be able to do this part of the assignment you will need to have a good understanding of the meaning of the interfaces and abstract classes, and why implementation inheritance or interface inheritance has been used.

You can see an example of creating another similar object structure in the setUp() method of the es.ua.dlsi.prog3.p5.model.DocumentTest unit test provided in the base project.

When you use the Heading constructor you should catch the exception EditorException and rethrow it as an unchecked exception because we are creating the classes and this exception should never be thrown because we are creating correct headers.

Unit tests

We provide unit tests in the test/ folder of the base project that check the proper behaviour of the classes. It is important that you understand what is tested and how it is done.

Documentation

It is not necessary to document this practical assignment

You can document the code you write, in fact we recommend it, but it will not be evaluated.

Minimal requirements for grading your assignment

  • Your program must run with no errors.
  • Unless otherwise stated, your program must not emit any kind of message or text through the standard output or standard error streams. Also avoid error output messages.
  • The format of the name of all properties must be strictly respected, both in terms of scope of visibility and in terms of type and way of writing. Make sure that you respect the distinction between class and instance attributes, as well as the uppercase and lowercase letters in the identifiers.
  • Your code must be conveniently documented and significant content has to be obtained after running the javadoc tool.

Submission of the assignment

Submission is done during the control to the DLSI practice server.

You must upload a compressed file with your source code (only .java files). In a terminal, place yourself in the ‘src’ directory of your Eclipse project and enter the command

tar czvf prog3-p5.tgz *

This will compress all the code in src/, including those classes that were already implemented. This is correct and should be delivered as is.

Upload the file prog3-p5.tgz to the practice server. Follow the instructions on the page to log in and upload your work.

Grading 

Testing of your assignment will be done automatically. This means that your program must strictly conform to the input and output formats given in this document, as well as to the public interfaces of all the classes: do not change the method signatures (name of the method, number, type and order of arguments, and data type returned) or their behaviour. So, for example, the Clase(int,int) method must have exactly two arguments of type int.

You can find more information about the grading of programming assignments in the subject description sheet.

In addition to the automatic grading, a plagiarism detection application will be used.

The applicable regulations of the University of Alicante Polytechnic School in the event of plagiarism are indicated below:


“Theoretical/practical work must be original. The detection of copy or plagiarism will suppose the qualification of”0" in the corresponding assignment. The corresponding Department and the University of Alicante Polytechnic School will be informed about the incident. Repeated conduct in this or any other subject will result in notification of the offences committed to the pertinent vice canchellor’s office so that they study the case and punish it in accordance with the legislation in force".


Clarifications

  • Although not recommended, you can add private attributes and methods to the classes. Notice, however, that you must implement ALL the methods indicated in this document and make sure that they work as expected, even if they are never called in your implementation. 
  • Any additional remark will be published in this page. It is recommended that you use this page as the primary source of the instructions.
  • If you want to know more, this assignment has been designed using design patterns, specifically the Composite, Visitor and Decorator patterns. Design patterns are not studied in this subject but in later courses.