Introduction
At this moment we have a TextProcessor, a LibraryBuilder as well as the Library
itself. As you read last week a Library is capable of producing pieces of text
in a simple way. We also briefly mentioned the BookMark which represents a
single paragraph of text. We haven't seen it's implementation yet. This is
the topic of this week's article part.
BookMark implementation
Since BookMarks are so strongly coupled with a Library I decided to implement
a BookMark as a nested non-static class. This allows for the implementation to
use all of the Library, including the private parts of it. Creatively I named
this class the BookMarkImpl. Because it's a nested class it is defined inside
the definition of the Library class like this:
Expand|Select|Wrap|Line Numbers
- public class Library {
- ...
- private class BookMarkImpl implements BookMark {
- ...
- }
- }
knows anything at all about this class. Just the BookMark interface is known
to everything else except for the Libraray, i.e. it knows about this class.
Also note that I didn't make this a Serializable class because if you serialize
an instance of this little class you immediately serialize the entire Library
object with it which is not what I want. It would be as if you're asking for
a little banana and it'd come with an entire gorilla attached to it.
The BookMarkImpl class has only one private int member, the paragraph number p.
Here is the first part of the definition:
Expand|Select|Wrap|Line Numbers
- private class BookMarkImpl implements BookMark {
- private int p;
- private BookMarkImpl(int p) {
- if (p < 0 || p >= paragraphs.length)
- throw new IndexOutOfBoundsException(
- "paragraph : !(0 <= "+p+" < "+paragraphs.length);
- this.p= p;
- }
object can construct a BookMarkImpl directly. From the previous article part
you saw that the Library can hand out BookMarks to the outside world, so the
paragraph number may be incorrect. That's why the BookMarkImpl class protects
itself against such errors by throwing an IndexOutOfBoundsException if the
parameter does not represent a valid paragraph number.
The BookMarkImpl implements a small convenience method for its own use:
Expand|Select|Wrap|Line Numbers
- private Library getLibrary() { return Library.this; }
Expand|Select|Wrap|Line Numbers
- public boolean equals(Object obj) {
- if (obj == null || !(obj instanceof BookMarkImpl)) return false;
- BookMarkImpl that= (BookMarkImpl)obj;
- return this.getLibrary() == that.getLibrary() &&
- this.p == that.p;
- }
stored in the same Library.
Of course a hashCode() method accompanies the equals() method. It is very simple:
Expand|Select|Wrap|Line Numbers
- public int hashCode() { return p; }
storage in a Map or Set.
Given that single paragraph number, a BookMarkImpl can return the absolute
group, book, chapter and of course the paragraph number itself. Here is how
it is done:
Expand|Select|Wrap|Line Numbers
- public int getGroupNumber() {
- int c= Library.this.getIndex(chapters, p);
- int b= Library.this.getIndex(books, c);
- return Library.this.getIndex(groups, b);
- }
- public int getBookNumber() {
- int c= Library.this.getIndex(chapters, p);
- return Library.this.getIndex(books, c);
- }
- public int getChapterNumber() {
- return Library.this.getIndex(chapters, p);
- }
- public int getParagraphNumber() {
- return p;
- }
itself. We saw its definition in the previous article part. Not only can a
BookMarkImpl return absolute numbers, it is also able to return relative
index numbers, i.e. the book number relative to the group for which the
paragraph is a 'member' etc. Here are the methods:
Expand|Select|Wrap|Line Numbers
- public int getRelativeBook() {
- int b= getBookNumber();
- return b-groups[getGroupNumber()].getIndex();
- }
- public int getRelativeChapter() {
- int c= getChapterNumber();
- return c-books[getBookNumber()].getIndex();
- }
- public int getRelativeParagraph() {
- return p-chapters[Library.this.getIndex(chapters, p)].getIndex();
- }
organization of a library and they are simply numbered 0, 1, 2 ... etc.
Observe the use of the getIndex() method again.
A BookMarkImpl can also return the text that belongs to its paragraph number;
not just the text for the paragraph itself but also for the chapter, book and
group to which the paragraph belongs:
Expand|Select|Wrap|Line Numbers
- public String getGroup() {
- return groups[getGroupNumber()].getName();
- }
- public String getBook() {
- return books[getBookNumber()].getName();
- }
- public String getChapter() {
- return chapters[getChapterNumber()].getName();
- }
- public String getParagraph() {
- return Library.this.decompress(paragraphs[p]);
- }
produce the actual text of the paragraph. These methods use the previously
defined methods to retrieve the correct absolute numbers given just that
single paragraph number.
A library supports 'notations' belonging to paragraphs; the user is free
to add, alter or remove any notation s/he wants. This is how a BookMarkImpl
offers the needed functionality:
Expand|Select|Wrap|Line Numbers
- public void putNote(String note) { notesMap.put(p, note); }
- public String getNote() { return notesMap.get(p); }
returns or sets the appropriate notation that belongs to the BookMark's
paragraph.
Finally, the BookMarkImpl implements the toString() method:
Expand|Select|Wrap|Line Numbers
- public String toString() {
- return getGroup()+sep+
- getBook()+sep+
- getChapter()+sep+
- getRelativeParagraph()+sep+
- getParagraph();
- }
the paragraph it represents. The 'sep' variable is a variable defined in the
Library class itself and is the separator text used between the group, book,
chapter and paragraph text. Here is how it is done in the Library class:
Expand|Select|Wrap|Line Numbers
- private static final String SEP= "\t";
- ...
- private String sep= SEP;
- ...
- public void setSeparator(String sep) { this.sep= sep; }
- public String getSeparator() { return sep; }
user can get and set the separator used by the toString() method of the
BookMarkImpl objects. The BookMarkImpl defines all the methods of the BookMark
interface and that's all the user knows about this nested class: it implements
this interface.
Concluding remarks
We have seen an implementation of the BookMark interface in this article part.
BookMarks are also used in the next part of this article: the Queries.
A Query implements the fun part of this little project, i.e. it allows us to
query the entire text in quite a flexible way using regular expressions and
a lot more.
I hope to see you again next week and,
kind regards,
Jos