t containing the model data representation.
	 */
	set( data ) {
		const root = this.root;
		const model = root.document.model;

		model.change( writer => {
			// Replace the document contents.
			writer.remove( writer.createRangeIn( root ) );
			writer.insert( createFragment( data, writer ), root );

			// Clean up previous document selection.
			writer.setSelection( null );
			writer.removeSelectionAttribute( model.document.selection.getAttributeKeys() );
		} );

		function createFragment( data, writer ) {
			const fragment = writer.createDocumentFragment();

			addChildren( data, fragment );

			return fragment;

			function addChildren( dataNode, modelTarget ) {
				const children = dataNode[ $children ];
				if ( children ) {
					let index = 0;
					let child;
					while ( ( child = children[ index ] ) ) {
						if ( $text in child ) {
							writer.appendText( child[ $text ], child[ $attribs ], modelTarget );
						} else {
							const element = writer.createElement( child[ $element ], child[ $attribs ] );

							// Go recursively.
							addChildren( child, element );

							writer.append( element, modelTarget );
						}

						index++;
					}
				}
			}
		}
	}
}

/**
 * Holds a deep object representation of model nodes.
 */
class Tree {
	/**
	 * Creates and instance of the Tree class with an empty root node.
	 */
	constructor() {
		/**
		 * The root node.
		 *
		 * @type {String[]}
		 */
		this.root = {};
	}

	/**
	 * Inserts a model node in the tree.
	 *
	 * The node position in the tree reflects its position in the model.
	 *
	 * @param modelNode {Element|Text} The model node to be inserted.
	 */
	insert( modelNode ) {
		const parent = this.getNode( modelNode.parent );
		const children = parent[ $children ] || ( parent[ $children ] = [] );
		children.splice( modelNode.index, 0, this.getDefinition( modelNode ) );
	}

	/**
	 * Remove a node from the tree based on its position.
	 *
	 * The position in the tree is calculated out of a model position.
	 *
	 * @param modelPosition {Position} The model posit