public interface NodeState
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.
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.
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.
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.
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.
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.
Modifier and Type | Field and Description |
---|---|
static com.google.common.base.Predicate<NodeState> |
EXISTS
Predicate that checks the existence of NodeState instances.
|
Modifier and Type | Method and Description |
---|---|
@NotNull NodeBuilder |
builder()
Returns a builder for constructing a new node state based on
this state, i.e.
|
boolean |
compareAgainstBaseState(NodeState base,
NodeStateDiff diff)
Compares this node state against the given base state.
|
boolean |
exists()
Checks whether this node exists.
|
boolean |
getBoolean(@NotNull java.lang.String name)
Returns the boolean value of the named property.
|
@NotNull NodeState |
getChildNode(@NotNull java.lang.String name)
Returns the named, possibly non-existent, child node.
|
long |
getChildNodeCount(long max)
Returns the number of iterable child nodes of this node.
|
@NotNull java.lang.Iterable<? extends ChildNodeEntry> |
getChildNodeEntries()
Returns the iterable child node entries of this instance.
|
java.lang.Iterable<java.lang.String> |
getChildNodeNames()
Returns the names of all iterable child nodes.
|
long |
getLong(java.lang.String name)
Returns the long value of the named property.
|
@Nullable java.lang.String |
getName(@NotNull java.lang.String name)
Returns the name value of the named property.
|
@NotNull java.lang.Iterable<java.lang.String> |
getNames(@NotNull java.lang.String name)
Returns the name values of the named property.
|
@NotNull java.lang.Iterable<? extends PropertyState> |
getProperties()
Returns an iterable of the properties of this node.
|
@Nullable PropertyState |
getProperty(@NotNull java.lang.String name)
Returns the named property, or
null if no such property exists. |
long |
getPropertyCount()
Returns the number of properties of this node.
|
@Nullable java.lang.String |
getString(java.lang.String name)
Returns the string value of the named property.
|
@NotNull java.lang.Iterable<java.lang.String> |
getStrings(@NotNull java.lang.String name)
Returns the string values of the named property.
|
boolean |
hasChildNode(@NotNull java.lang.String name)
Checks whether the named child node exists.
|
boolean |
hasProperty(@NotNull java.lang.String name)
Checks whether the named property exists.
|
static final com.google.common.base.Predicate<NodeState> EXISTS
boolean exists()
true
if this node exists, false
if notboolean hasProperty(@NotNull @NotNull java.lang.String name)
getProperty(name) != null
, but may be optimized
to avoid having to load the property value.name
- property nametrue
if the named property exists,
false
otherwise@Nullable @Nullable PropertyState getProperty(@NotNull @NotNull java.lang.String name)
null
if no such property exists.name
- name of the property to returnnull
if not foundboolean getBoolean(@NotNull @NotNull java.lang.String name)
PropertyState property = state.getProperty(name);
return property != null
&& property.getType() == Type.BOOLEAN
&& property.getValue(Type.BOOLEAN);
name
- property namefalse
long getLong(java.lang.String name)
PropertyState property = state.getProperty(name);
if (property != null && property.getType() == Type.LONG) {
return property.getValue(Type.LONG);
} else {
return 0;
}
name
- property name@Nullable @Nullable java.lang.String getString(java.lang.String name)
PropertyState property = state.getProperty(name);
if (property != null && property.getType() == Type.STRING) {
return property.getValue(Type.STRING);
} else {
return null;
}
name
- property namenull
@NotNull @NotNull java.lang.Iterable<java.lang.String> getStrings(@NotNull @NotNull java.lang.String name)
PropertyState property = state.getProperty(name);
if (property != null && property.getType() == Type.STRINGS) {
return property.getValue(Type.STRINGS);
} else {
return Collections.emptyList();
}
name
- property name@Nullable @Nullable java.lang.String getName(@NotNull @NotNull java.lang.String name)
PropertyState property = state.getProperty(name);
if (property != null && property.getType() == Type.NAME) {
return property.getValue(Type.NAME);
} else {
return null;
}
name
- property namenull
@NotNull @NotNull java.lang.Iterable<java.lang.String> getNames(@NotNull @NotNull java.lang.String name)
PropertyState property = state.getProperty(name);
if (property != null && property.getType() == Type.NAMES) {
return property.getValue(Type.NAMES);
} else {
return Collections.emptyList();
}
name
- property namelong getPropertyCount()
@NotNull @NotNull java.lang.Iterable<? extends PropertyState> getProperties()
boolean hasChildNode(@NotNull @NotNull java.lang.String name)
getChildNode(name).exists()
, except that
passing an invalid name as argument will result in a false
return value instead of an IllegalArgumentException
.name
- name of the child nodetrue
if the named child node exists,
false
otherwise@NotNull @NotNull NodeState getChildNode(@NotNull @NotNull java.lang.String name) throws java.lang.IllegalArgumentException
exists()
method on the returned child node to determine
whether the node exists or not.name
- name of the child node to returnjava.lang.IllegalArgumentException
- if the given name string is is empty
or contains a forward slash characterlong getChildNodeCount(long max)
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).
max
- the maximum number of entries to traversejava.lang.Iterable<java.lang.String> getChildNodeNames()
@NotNull @NotNull java.lang.Iterable<? extends ChildNodeEntry> getChildNodeEntries()
Note on cost and performance: while it is possible to iterate over
all child NodeState
s 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.
@NotNull @NotNull NodeBuilder builder()
boolean compareAgainstBaseState(NodeState base, NodeStateDiff diff)
TODO: Define the behavior of this method with regards to iterability/existence of child nodes.
base
- base statediff
- handler of node state differencestrue
if the full diff was performed, or
false
if it was aborted as requested by the handler
(see the NodeStateDiff
contract for more details)Copyright © 2010 - 2020 Adobe. All Rights Reserved