Skip to content

Instantly share code, notes, and snippets.

@jukka
Created February 28, 2012 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jukka/1932695 to your computer and use it in GitHub Desktop.
Save jukka/1932695 to your computer and use it in GitHub Desktop.
jr3 tree model
import java.util.Map;
/**
* Trees are the key concept in a hierarchical content repository.
* This interface is a low-level tree node representation that just
* maps zero or more string names to corresponding child nodes.
* Depending on context, a Tree instance can be interpreted as
* representing just that tree node, the subtree starting at that node,
* or an entire tree in case it's a root node.
* <p>
* For familiarity and easy integration with existing libraries this
* interface extends the generic {@link Map} interface instead of
* providing a custom alternative. Note also that this interface is
* named Tree instead of something like Item or Node to avoid confusion
* with the related JCR interfaces.
* </p>
*
* <h2>Leaves and non-leaves</h2>
* <p>
* Normal tree nodes only contain structural information expressed as
* the set of child nodes and their names. The content of a tree, expressed
* in data types like strings, numbers and binaries, is stored in special
* leaf nodes with no children. Such leaf nodes implement the {@link Leaf}
* sub-interface and can be identified and accessed using the
* {@link #isLeaf()} and {@link #asLeaf()} methods.
* </p>
* <p>
* Note that even tough such leaf nodes are guaranteed to have no children
* (i.e. {@link #isLeaf()} implies {@link #isEmpty()}), the reverse is not
* necessarily true. It's possible for a non-leaf node to contain no children,
* though such cases occur normally only transiently when new subtrees are
* being constructed.
* </p>
*
* <h2>Mutability and thread-safety</h2>
* <p>
* Tree objects are immutable by default and thus safe for concurrent access.
* Using a mutator method like {@link #clear()} or {@link #put(String, Tree)}
* results in an {@link UnsupportedOperationException exception}. A new Tree
* instance is needed to express a modified content tree. As a result it's
* safe to repeat operations like iterating over all child nodes of a Tree
* instance and expect results to be the same.
* </p>
* <p>
* In specific situations like when constructing new trees it's possible for
* Tree instances to be mutable. Such cases need to be explicitly documented
* and managed in a way that prevents thread-safety issues, for example by
* keeping a reference to such a mutable Tree instance local to a single
* thread.
* </p>
*
* <h2>Persistence and error-handling</h2>
* <p>
* A Tree instance can be (and often is) backed by local files or network
* resources. All IO operations or related concerns like caching should be
* handled transparently below this interface. Potential IO problems and
* recovery attempts like retrying a timed-out network access need to be
* handled below this interface, and only hard errors should be thrown up
* as {@link RuntimeException unchecked exceptions} that higher level code
* is not expected to be able to recover from.
* </p>
* <p>
* Since this interface exposes no higher level constructs like access
* controls, locking, node types or even path parsing, there's no way
* for content access to fail because of such concerns. Such functionality
* and related checked exceptions or other control flow constructs should
* be implemented on a higher level above this interface.
* </p>
*
* <h2>Decoration and virtual content</h2>
* <p>
* Not all content exposed by Tree objects needs to be backed by actual
* persisted data. An implementation may want to provide provide derived
* data like for example the aggregate size of the entire subtree as an
* extra virtual leaf node. A virtualization, sharding or caching layer
* could provide a composite view over multiple underlying content trees.
* Or a basic access control layer could decide to hide certain content
* based on specific rules. All such features need to be implemented
* according to the API contract of this interface. A separate higher level
* interface needs to be used if an implementation can't for example
* guarantee immutability of exposed content as discussed above.
* </p>
*/
interface Tree extends Map<String, Tree> {
/**
* Checks whether this is a {@link Leaf} instance. Can be used to
* control program flow without explicit <code>instanceof</code> checks
* for handling leaf content. See also the {@link #asLeaf()} method
* that can additionally take care of type casting.
*
* @return <code>true</code> if this is a {@link Leaf},
* <code>false</code> if not
*/
boolean isLeaf();
/**
* Returns this instance as a {@link Leaf} if possible. Can be used
* to access leaf nodes without <code>instanceof</code> checks or
* explicit type casting. A typical access pattern is:
* <pre>
* Leaf leaf = tree.asLeaf();
* if (leaf != null) {
* // handle leaf content
* } else {
* // handle non-leaf content
* }
* </pre>
*
* @return this instance as a {@link Leaf},
* or <code>null</code> if this is a non-leaf node
*/
Leaf asLeaf();
}
/**
* Leaves are special {@link Tree} nodes contain typed data like strings,
* numbers, binaries, etc. This interface extends {@link Tree} and thus
* also {@link Map}, but all Leaf instances are guaranteed to contain zero
* child nodes. Leaves are always immutable.
*/
interface Leaf extends Tree {
// TODO: Add data access methods
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment