Interface NodeState

  • All Known Implementing Classes:
    AbstractDecoratedNodeState, AbstractNodeState, EmptyNodeState, FilteringNodeState, ModifiedNodeState, ReportingNodeState

    public interface NodeState
    A node in a content tree consists of child nodes and properties, each of which evolves through different states during its lifecycle. This interface represents a specific, immutable state of a node.

    The state of a node consists of named properties and child nodes. Names are non-empty strings that never contain the forward slash character, "/". Implementations may place additional restrictions on possible name strings. The properties and child nodes are unordered, and no two properties or two child nodes may have the same name. An implementation may additionally restrict a property and a child node from having the same name.

    Depending on context, a NodeState instance can be interpreted as representing the state of just that node, of the subtree starting at that node, or of an entire tree in case it's a root node.

    The crucial difference between this interface and the similarly named class in Jackrabbit 2.x is that this interface represents a specific, immutable state of a node, whereas the Jackrabbit 2.x class represented the current state of a node.

    Immutability and thread-safety

    As mentioned above, all node and property states are always immutable. Thus repeating a method call is always guaranteed to produce the same result as before unless some internal error occurs (see below). This immutability only applies to a specific state instance. Different states of a node can obviously be different, and in some cases even different instances of the same state may behave slightly differently. For example due to performance optimization or other similar changes the iteration order of properties or child nodes may be different for two instances of the same state.

    In addition to being immutable, a specific state instance is guaranteed to be fully thread-safe. Possible caching or other internal changes need to be properly synchronized so that any number of concurrent clients can safely access a state instance.

    Persistence and error-handling

    A node state 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 unchecked exceptions that higher level code is not expected to be able to recover from.

    Since this interface exposes no higher level constructs like 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. On the other hand read access controls can be implemented below this interface, in which case some content that would otherwise be accessible might not show up through such an implementation.

    Existence and iterability of node states

    The getChildNode(String) method is special in that it never returns a null value, even if the named child node does not exist. Instead a client should use the exists() method on the returned child state to check whether that node exists. The purpose of this separation of concerns is to allow an implementation to lazily load content only when it's actually read instead of just traversed. It also simplifies client code by avoiding the need for many null checks when traversing paths.

    The iterability of a node is a related concept to the above-mentioned existence. A node state is iterable if it is included in the return values of the getChildNodeCount(long), getChildNodeNames() and getChildNodeEntries() methods. An iterable node is guaranteed to exist, though not all existing nodes are necessarily iterable.

    Furthermore, a non-existing node is guaranteed to contain no properties or iterable child nodes. It can, however contain non-iterable children. Such scenarios are typically the result of access control restrictions.

    Decoration and virtual content

    Not all content exposed by this interface needs to be backed by actual persisted data. An implementation may want to provide derived data, like for example the aggregate size of the entire subtree as an extra virtual property. A virtualization, sharding or caching layer could provide a composite view over multiple underlying trees. Or a an 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.

    Equality and hash codes

    Two node states are considered equal if and only if their existence, properties and iterable child nodes match, regardless of ordering. The Object.equals(Object) method needs to be implemented so that it complies with this definition. And while node states are not meant for use as hash keys, the Object.hashCode() method should still be implemented according to this equality contract.

    • Field Detail

      • EXISTS

        static final Predicate<NodeState> EXISTS
        Predicate that checks the existence of NodeState instances.
    • Method Detail

      • exists

        boolean exists()
        Checks whether this node exists. See the above discussion about the existence of node states.
        Returns:
        true if this node exists, false if not
      • hasProperty

        boolean hasProperty​(@NotNull
                            @NotNull String name)
        Checks whether the named property exists. The implementation is equivalent to getProperty(name) != null, but may be optimized to avoid having to load the property value.
        Parameters:
        name - property name
        Returns:
        true if the named property exists, false otherwise
      • getProperty

        @Nullable
        @Nullable PropertyState getProperty​(@NotNull
                                            @NotNull String name)
        Returns the named property, or null if no such property exists.
        Parameters:
        name - name of the property to return
        Returns:
        named property, or null if not found
      • getBoolean

        boolean getBoolean​(@NotNull
                           @NotNull String name)
        Returns the boolean value of the named property. The implementation is equivalent to the following code, but may be optimized.
         
         PropertyState property = state.getProperty(name);
         return property != null
             && property.getType() == Type.BOOLEAN
             && property.getValue(Type.BOOLEAN);
         
         
        Parameters:
        name - property name
        Returns:
        boolean value of the named property, or false
      • getLong

        long getLong​(String name)
        Returns the long value of the named property. The implementation is equivalent to the following code, but may be optimized.
         
         PropertyState property = state.getProperty(name);
         if (property != null && property.getType() == Type.LONG) {
             return property.getValue(Type.LONG);
         } else {
             return 0;
         }
         
         
        Parameters:
        name - property name
        Returns:
        long value of the named property, or zero
      • getString

        @Nullable
        @Nullable String getString​(String name)
        Returns the string value of the named property. The implementation is equivalent to the following code, but may be optimized.
         
         PropertyState property = state.getProperty(name);
         if (property != null && property.getType() == Type.STRING) {
             return property.getValue(Type.STRING);
         } else {
             return null;
         }
         
         
        Parameters:
        name - property name
        Returns:
        string value of the named property, or null
      • getStrings

        @NotNull
        @NotNull Iterable<String> getStrings​(@NotNull
                                             @NotNull String name)
        Returns the string values of the named property. The implementation is equivalent to the following code, but may be optimized.
         
         PropertyState property = state.getProperty(name);
         if (property != null && property.getType() == Type.STRINGS) {
             return property.getValue(Type.STRINGS);
         } else {
             return Collections.emptyList();
         }
         
         
        Parameters:
        name - property name
        Returns:
        string values of the named property, or an empty collection
      • getName

        @Nullable
        @Nullable String getName​(@NotNull
                                 @NotNull String name)
        Returns the name value of the named property. The implementation is equivalent to the following code, but may be optimized.
         
         PropertyState property = state.getProperty(name);
         if (property != null && property.getType() == Type.NAME) {
             return property.getValue(Type.NAME);
         } else {
             return null;
         }
         
         
        Parameters:
        name - property name
        Returns:
        name value of the named property, or null
      • getNames

        @NotNull
        @NotNull Iterable<String> getNames​(@NotNull
                                           @NotNull String name)
        Returns the name values of the named property. The implementation is equivalent to the following code, but may be optimized.
         
         PropertyState property = state.getProperty(name);
         if (property != null && property.getType() == Type.NAMES) {
             return property.getValue(Type.NAMES);
         } else {
             return Collections.emptyList();
         }
         
         
        Parameters:
        name - property name
        Returns:
        name values of the named property, or an empty collection
      • getPropertyCount

        long getPropertyCount()
        Returns the number of properties of this node.
        Returns:
        number of properties
      • getProperties

        @NotNull
        @NotNull Iterable<? extends PropertyState> getProperties()
        Returns an iterable of the properties of this node. Multiple iterations are guaranteed to return the properties in the same order, but the specific order used is implementation-dependent and may change across different states of the same node.
        Returns:
        properties in some stable order
      • hasChildNode

        boolean hasChildNode​(@NotNull
                             @NotNull String name)
        Checks whether the named child node exists. The implementation is equivalent to getChildNode(name).exists(), except that passing an invalid name as argument will result in a false return value instead of an IllegalArgumentException.
        Parameters:
        name - name of the child node
        Returns:
        true if the named child node exists, false otherwise
      • getChildNode

        @NotNull
        @NotNull NodeState getChildNode​(@NotNull
                                        @NotNull String name)
                                 throws IllegalArgumentException
        Returns the named, possibly non-existent, child node. Use the exists() method on the returned child node to determine whether the node exists or not.
        Parameters:
        name - name of the child node to return
        Returns:
        named child node
        Throws:
        IllegalArgumentException - if the given name string is is empty or contains a forward slash character
      • getChildNodeCount

        long getChildNodeCount​(long max)
        Returns the number of iterable child nodes of this node.

        If an implementation knows the exact value, it returns it (even if the value is higher than max). If the implementation does not know the exact value, and the child node count is higher than max, it may return Long.MAX_VALUE. The cost of the operation is at most O(max).

        Parameters:
        max - the maximum number of entries to traverse
        Returns:
        number of iterable child nodes
      • getChildNodeNames

        Iterable<String> getChildNodeNames()
        Returns the names of all iterable child nodes.
        Returns:
        child node names in some stable order
      • getChildNodeEntries

        @NotNull
        @NotNull Iterable<? extends ChildNodeEntry> getChildNodeEntries()
        Returns the iterable child node entries of this instance. Multiple iterations are guaranteed to return the child nodes in the same order, but the specific order used is implementation dependent and may change across different states of the same node.

        Note on cost and performance: while it is possible to iterate over all child NodeStates with the two methods getChildNodeNames() and getChildNode(String), this method is considered more efficient because an implementation can potentially perform the retrieval of the name and NodeState in one call. This results in O(n) vs. O(n log n) when iterating over the child node names and then look up the NodeState by name.

        Returns:
        child node entries in some stable order
      • builder

        @NotNull
        @NotNull NodeBuilder builder()
        Returns a builder for constructing a new node state based on this state, i.e. starting with all the properties and child nodes of this state.
        Returns:
        node builder based on this state
        Since:
        Oak 0.6
      • compareAgainstBaseState

        boolean compareAgainstBaseState​(NodeState base,
                                        NodeStateDiff diff)
        Compares this node state against the given base state. Any differences are reported by calling the relevant added/changed/deleted methods of the given handler.

        TODO: Define the behavior of this method with regards to iterability/existence of child nodes.

        Parameters:
        base - base state
        diff - handler of node state differences
        Returns:
        true if the full diff was performed, or false if it was aborted as requested by the handler (see the NodeStateDiff contract for more details)
        Since:
        0ak 0.4, return value added in 0.7