Aspose.Words

Design Patterns in Aspose.Words

For a better understanding of the Aspose.Words object model, the design patterns used in the public interfaces are described here. The links to online descriptions of the patterns are provided where possible, but of course, for the best coverage see the GoF book if this is one of their patterns.

Document Object Model is a Composite

General Composite related ideas:

·          Node is the base class for all nodes.

·          CompositeNode is the base class for composite nodes.

·          In our implementation, the base Node class does not have the child management nodes in its interface. The child management methods appear only in CompositeNode.

·          We found that removing the child management methods from the base class made interfaces much cleaner and did not bring in a lot of extra type casting

Here is a description of the Composite pattern in Wikipedia

Aspose.Words specific:

·          Many methods and properties of Node and CompositeNode were designed to be similar to XmlDocument, XmlNode and XmlElement intentionally to help shorten the learning curve.

·          The Aspose.Words.Document class is the root node for a complete Word document.

·          A node always belongs to a Document even if it is "detached" from the tree and does not have a parent node. This is needed because the node might have some formatting properties that are valid only in the context of a specific Document.

·          When moving or copying nodes between different documents you need to use Document.ImportNode before you can insert a node from a different document.

·          CompositeNode.ChildNodes, CompositeNode.GetChildNodes return NodeCollection, which is a wrapper that represents a selection of nodes as a live collection.

·          Document.Sections, Section.HeadersFooters, Story.Paragraphs and so on are typed-wrapper collections that derive from NodeCollection and provide typed access to a selection of nodes of a specific type.

DocumentBuilder is a Builder for a Composite

Generally, it is easy to work the document tree directly, inserting and removing nodes where you want them.

Example CreateAndAddParagraphNode

Creates and adds a paragraph node.

[Java]

 

Document doc = new Document();

 

Paragraph para = new Paragraph(doc);

 

Section section = doc.getLastSection();

section.getBody().appendChild(para);

 

 

However, there are cases where creating a document element directly is not so straightforward and it is better to have some utility that will do the creation for you. For example to create a Word field several nodes need to be inserted, and you should make sure they are all in an appropriate state: FieldStart, Run for the field code, FieldSeparator, one or more Run nodes for the field result and FieldEnd. Inserting a form field is even more complex; it needs a complete Word field as well as FormField, BookmarkStart and BookmarkEnd nodes.

DocumentBuilder is the tool that makes the process of building a document simpler. There are two groups of methods: to move the cursor to a node where you want to do the building, and to insert something at the cursor.

Although DocumentBuilder does not exactly fulfill the intent of the Builder pattern (the builder pattern is used to enable the creation of a variety of complex objects from one source object), we still call it Builder because that is what it does.

Range is a Facade for a Composite

A text document with a complex structure and formatting such as a Microsoft Word document is hard to represent in an easy and user-friendly object model.

We choose to represent it as a tree of nodes because it gives the users of Aspose.Words what they want - detailed access to the document content in a reasonably familiar environment (XmlDocument-like API) and makes it possible for us to actually do it (unlike an API similar to Microsoft Word Automation that we wanted initially).

Therefore, you have the tool to examine and modify Word files, but it turns out some operations on "flat text" are quite hard to do with a "tree model". Such seemingly easy things as find and replace, delete a paragraph or a section break can require significant efforts to traverse the tree, split and join tree nodes and so on.

The Range class (although still in its infancy) is designed to hide the "tree look" of the model behind a "flat text" interface. For example, Range provides find and replace functionality that can search and replace across different Run, Paragraph, Table etc nodes and it hides a lot behind the scenes as it has to cut, move and join nodes of the tree as it goes. We think Range is clearly a Façade pattern.

More Facades for Various Document Elements

Bookmark is a Facade that allows you to work with two nodes BookmarkStart and BookmarkEnd as a single entity.

DocumentVisitor is a Visitor

The Visitor pattern is famous for its ability to allow the addition of new operations to an existing object model without modifying this model

Just derive from DocumentVisitor, override the VisitXXX methods such as DocumentVisitor.VisitParagraphStart and DocumentVisitor.VisitRun that receive the calls for the desired nodes. Call Node.Accept on the node from which you want to start enumeration and it will all work. You can even return a value from your VisitXXX methods to indicate how the enumeration should continue.

We also extensively use DocumentVisitor ourselves:

·          All export converters DOC, HTML and PDF inside Aspose.Words are implemented as document visitors.

·          Internal field and bookmark finders, and revision accepting engine are all implemented as document visitors.