Class ReferencePath
Overview
A reference path defines a path of object references, starting from an instance of some starting object type and ending up at one or more target objects (having some target object types), by hopping from object to object through a sequence of reference fields. The reference fields may be simple reference fields or sub-fields of complex fields (i.e., list or set element, or map key or value), and may be traversed in either direction (forward or inverse). At its heart, a reference path is simply a list of reference fields along with a flag for each field that determines in which direction the reference should be traversed.
At each step in the path, there is a set of possible current object types: at the initial step, this set is just the starting object type, and after the final step, this set equals the target object types. It is possible for any step to only apply to some of the current object types. This can happen when the field is only defined in some of the object types (for forward step) or when the field can only refer to some of them (for inverse steps). In these cases, the search ends for any unmatched types and continues for matching types. However, it is an error if, at any step, no types match, which would mean no objects could ever be found. It's also an error if the field specified at any step is ambiguous, which can happen when two sub-types define incompatible fields using the same name.
When stepping through a collection field in the forward direction, or through any field in the inverse direction, the number of objects can multiply. In general, the number of target objects can be vastly different than the number of starting objects, depending on the fan-in/fan-out of the references traversed.
Reference Steps
Reference paths are denoted in String
form as zero or more concatenated reference steps:
- Forward reference steps are denoted
"->fieldName"
wherefieldName
is the name of a reference field defined in at least one of the current object types. - Inverse reference steps are denoted
"<-TypeName.fieldName"
wherefieldName
is the name of a reference field defined inTypeName
(or some sub-type).
Field Names
In the case of complex fields, the fieldName
must specify the sub-field:
- For List and Set fields, the only sub-field is
element
; for these field types,myfield
can be used as an abbreviation formyfield.element
. - For Map fields, either
myfield.key
ormyfield.value
must be specified.
In rare cases, sub-types of a common super-type type have fields with the same name but different storage IDs.
To disambiguate, the storage ID may always be explicitly specified as a suffix like this: "myfield#123"
.
Type Names
For inverse steps, the TypeName
must be given to specify the type of the referring object.
TypeName
is either the name of an object type in the schema (typically this is the unqualified
class name of the corresponding Java model class), or any fully-qualified Java class name.
Examples
Consider the following model classes:
public interface Animal<T extends Animal<T>> { T getParent(); void setParent(T parent); Set<Animal<?>> getEnemies(); } @PermazenType public interface Elephant extends Animal<Elephant> { Elephant getFriend(); void setFriend(Elephant friend); } @PermazenType public interface Giraffe extends Animal<Giraffe> { Giraffe getFriend(); void setFriend(Giraffe friend); }Then these paths have the following meanings:
Start Type | Path | Target Types | Description |
---|---|---|---|
Elephant |
"" |
Elephant |
The starting Elephant |
Elephant |
"->parent" |
Elephant |
The Elephant 's parent |
Giraffe |
"->parent" |
Giraffe |
The Giraffe 's parent |
Animal |
"->parent" |
Elephant , Giraffe |
The Animals 's parent |
Elephant |
"<-Elephant.enemies" |
Elephant |
All Elephant s for whom the original Elephant is an enemy |
Elephant |
"<-Animal.enemies" |
Elephant , Giraffe |
All Animal s for whom the original Elephant is an enemy |
Elephant |
"<-friend" |
Elephant |
All Elephant s for whom the original Elephant is their friend |
Animal |
"<-friend" |
Elephant , Giraffe |
All Animal s for whom the original Animal is their friend |
Elephant |
"->friend<-Giraffe.enemies" |
Giraffe |
All Giraffe s for whom the original Elephant 's friend is an enemy |
Elephant |
"->enemies<-Giraffe.friend ->enemies<-Elephant.friend" |
Elephant |
All Elephant s who's friend is an enemy of some Giraffe for whom one of the original
Elephant 's enemies is their friend |
Elephant |
"<-Giraffe.friend" |
N/A | Invalid - it's not possible for an Elephant to be a Giraffe 's friend |
Using Reference Paths
Reference paths may be explicitly created via Permazen.parseReferencePath()
and traversed in the forward direction via
PermazenTransaction.followReferencePath()
or in the inverse direction via PermazenTransaction.invertReferencePath()
.
Reference paths are also used by @OnChange
annotations to specify non-local objects
for change monitoring, and by @ReferencePath
annotations.
-
Method Summary
Modifier and TypeMethodDescriptionboolean
List<Set<PermazenClass<?>>>
Get the current object types at each step in the path.int[]
Get the storage IDs of the reference fields in this path in the order they occur.Class<?>
Get the narrowest possible Java type of the object(s) at which this path starts.Set<PermazenClass<?>>
Get the possible model object types for the objects at the start of this path.Class<?>
Get the narrowest possible Java type of the object(s) at which this path ends.Set<PermazenClass<?>>
Get the possible model object types for the objects at the end of this path.int
hashCode()
boolean
isEmpty()
Determine whether this path is empty, i.e., contains zero steps.boolean
Determine whether traversing this path can result in only one object being found.int
size()
Get the number of steps in this reference path.toString()
Get theString
form of the path associated with this instance.
-
Method Details
-
getStartingTypes
Get the possible model object types for the objects at the start of this path.This method returns the first set in the list returned by
getCurrentTypesList()
. If this path has zero length, then this method returns the same set asgetTargetTypes()
.The returned set will contain a null element when the target object can possibly be an
UntypedPermazenObject
.- Returns:
- non-empty set of model object types at which this reference path starts, possibly including null
-
getStartingType
Get the narrowest possible Java type of the object(s) at which this path starts.The returned type will be as narrow as possible while still including all possibilities, but note that it's possible for there to be multiple candidates for the "starting type", none of which is a sub-type of any other. To retrieve all such starting types, use
getStartingTypes()
; this method just invokesUtil.findLowestCommonAncestorOfClasses()
on that result.- Returns:
- the Java type at which this reference path starts
-
getTargetTypes
Get the possible model object types for the objects at the end of this path.This method returns the last set in the list returned by
getCurrentTypesList()
. If this path has zero length, then this method returns the same set asgetStartingTypes()
.The returned set will contain a null element when the target object can possibly be an
UntypedPermazenObject
.- Returns:
- non-empty set of model object types at which this reference path ends, possibly including null
-
getTargetType
Get the narrowest possible Java type of the object(s) at which this path ends.The returned type will be as narrow as possible while still including all possibilities, but note that it's possible for there to be multiple candidates for the "target type", none of which is a sub-type of any other. To retrieve all such target types, use
getTargetTypes()
; this method just invokesUtil.findLowestCommonAncestorOfClasses()
on that result.- Returns:
- the Java type at which this reference path ends
-
getCurrentTypesList
Get the current object types at each step in the path.The returned list always has length one more than the length of the array returned by
getReferenceFields()
, such that the set at index i contains all possible types found after the ith step. The first element contains the starting object types and the last element contains the target types.A set in the list will contain a null element if an object at that step can possibly be an
UntypedPermazenObject
.- Returns:
- list of the possible
PermazenClass
's appearing at each step in this path, each of which is non-empty and may include null
-
getReferenceFields
public int[] getReferenceFields()Get the storage IDs of the reference fields in this path in the order they occur.Storage ID's will be negated to indicate reference fields traversed in the reverse direction.
The result will be empty if this path is empty.
- Returns:
- zero or more possibly negated reference field storage IDs
-
isSingular
public boolean isSingular()Determine whether traversing this path can result in only one object being found.An empty path is always singular - it always returns just the starting object.
- Returns:
- true if this path only includes forward simple field references, otherwise false
-
size
public int size()Get the number of steps in this reference path.- Returns:
- the length of this path
-
isEmpty
public boolean isEmpty()Determine whether this path is empty, i.e., contains zero steps.- Returns:
- true if this path is empty
-
toString
Get theString
form of the path associated with this instance. -
equals
-
hashCode
public int hashCode()
-