Programming 3
University of Alicante, 2024–2025
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.
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
- Block
- Block
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
).
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
.
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:
- First element of a numbered or ordered list.
- 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
.
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.
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.
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
.
Images
The first and simplest of the elements of type inline is the image, as can be seen in the UML class diagram below.
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.
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.
Below you have the full diagram with all the classes and interfaces we have introduced.
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) {
.export(this);
e}
····}
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
.
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.
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:
= new BoldTextDecorator(new Text("Texto")); IText element
If we wanted to create a text in boldface and italics we would just surround the bold with an italic:
= new ItalicsTextDecorator(new BoldTextDecorator(new Text("Texto"))); IText element
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).
Hyperlinks
We’ll do something similar to add links to both text and images.
A LinkParagraphContentDecorator
, through
AbstractParagraphContentDecorator
, is an
IParagraphContent
. If we wanted to replace an image with an
image that has a hyperlink we would do:
= new LinkParagraphContentDecorator(new Image("logo_ua.png", "UA Logo"), "https://www.ua.es/"); IParagraphContent imageWithLink
Below you have the classes and interfaces introduced in the two class diagrams shown in Figures 14 and 15:
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 aBreakLine
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 theIParagraphContent
interface. - The system includes ways to specify boldface and italics with the
BoldTextDecorator
andItalicsTextDecorator
classes. You must add theStrikeThroughDecorator
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 asthis 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 fromParagraph
. Note that making it inherit fromParagraph
is not decorating it but replacing how it is exported. To do this you must overload theexport
method and add theexport(Quote)
method inIExporter
. 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);
.
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.