public class ReferencePath extends Object
A reference path is a String
specifying a path from some implicit starting object type, through zero
or more reference fields, ending up at some target object type(s).
In other words, given some starting object(s), a reference path takes you through a path of references to the target object(s).
Note that 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.
Specifying Fields
The starting object type is always implicit from the context in which the path is used. The String
path describes
the reference fields, i.e., the "steps" in the path that travel from the starting object type to the target object type.
The steps are separated by dot ("."
) characters.
The reference fields in the path may be traversed in either the forward or inverse direction:
to traverse a field in the forward direction, specify name of the field; to traverse a field in the inverse direction,
specify the name of the field and its containing type using the syntax ^Type:field^
.
For complex fields, specify both the field and sub-field: for Set
and List
fields, the sub-field is
always "element"
, while for Map
fields the sub-field is either "key"
or "value"
.
For example, to traverse a map field's key
sub-field, specify "mymap.key"
.
Target Fields
A reference path may optionally have a target field appended to the end of the path. The target field does not have to be a reference field. If a target field is specified, the target object types are restricted to those types containing the target field.
Note that to avoid ambiguity it must be known at the time the path is parsed whether the path contains a target field: for
example, consider the path "parent.parent"
in the context of a Child
object: if there is a target field,
the target object is the child's parent and the target field is the "parent"
field of the child's parent
(which just happens to also be a reference field), but if there is no target field, the path simply refers to the child's
grandparent.
Examples
Considering the following model classes:
@PermazenType public class Employee { public abstract String getName(); public abstract void setName(String name); public abstract Employee getManager(); public abstract void setManager(Employee manager); public abstract Set<Asset> getAssets(); } @PermazenType public class Asset { public abstract int getAssetId(); public abstract void setAssetId(int assetId); public abstract String getName(); public abstract void setName(String name); }Then these paths have the following meanings:
Start Type | Path | Has Target Field? | Description |
---|---|---|---|
Employee |
"" |
No | The Employee |
Employee |
"" |
Yes | Invalid - no target field specified |
Employee |
"name" |
Yes | The Employee 's name |
Employee |
"name" |
No | Invalid - "name" is not a reference field |
Asset |
"name" |
Yes | The Asset 's name |
Object |
"name" |
Yes | The Employee 's or Asset 's name |
Employee |
"manager" |
No | The Employee 's manager |
Employee |
"manager" |
Yes | The "manager" field of the Employee |
Employee |
"manager.name" |
Yes | The Employee 's manager's name |
Employee |
"^Employee:manager^.name" |
Yes | The names of all of the Employee 's direct reports |
Employee |
"manager.assets.description" |
Yes | The descriptions of the Employee 's manager's assets |
Employee |
"manager.^Employee:manager^" |
No | All of the Employee 's manager's direct reports |
Asset |
"^Employee:assets.element^" |
No | The employee owning the Asset |
Asset |
"^Employee:assets.element^" |
Yes | Invalid - no target field specified |
Asset |
"^Employee:assets.element^ .manager.^Employee:manager^.asset.assetId" |
Yes | ID's of all Asset s owned by direct reports of the manager of the Employee owning the original
Asset |
Fields of Sub-Types
The same field can appear in multiple types, e.g., "name"
in the example above appears in both Employee
and Asset
. The set of all possible object types is recalculated at each step in the reference path, including
at the last step, which gives the target object type(s). At each intermediate step, as long as the Java types do not
contain incompatible definitions for the named field, the step is valid.
In rare cases where multiple sub-types of a common super-type type have fields with the same name but different storage IDs,
the storage ID may be explicitly specified as a suffix, for example, "name#123"
.
Using Reference Paths
Reference paths may be explicitly created via Permazen.parseReferencePath()
and traversed in the forward direction via JTransaction.followReferencePath()
or in the inverse direction via JTransaction.invertReferencePath()
.
Reference paths are also used implicitly by @OnChange
annotations to
specify non-local objects for change monitoring, and by @FollowPath
annotations.
Modifier and Type | Method and Description |
---|---|
List<Set<Class<?>>> |
getPathTypes()
Get the set of possible model object types at each step in the path.
|
int[] |
getReferenceFields()
Get the storage IDs of the reference fields in this path.
|
Class<?> |
getStartType()
Get the Java type of the object at which this path starts.
|
int |
getTargetField()
Get the storage ID associated with the target field, if any, in the target object type.
|
Set<TypeToken<?>> |
getTargetFieldTypes()
Get the Java type(s) corresponding to the target field at which this path ends, if any.
|
int |
getTargetSuperField()
Get the storage ID associated with the complex field containing the target field,
if any, in the case that the target field is a sub-field of a complex field.
|
Class<?> |
getTargetType()
Get the Java type of the object at which this path ends.
|
Set<Class<?>> |
getTargetTypes()
Get the possible Java types of the object at which this path ends.
|
String |
toString()
Get the
String form of the path associated with this instance. |
public Class<?> getStartType()
If there are zero reference fields in this path, then this will equal the target object type, or possibly a super-type if the target field exists only in a sub-type.
public Class<?> getTargetType()
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 invokes
Util.findLowestCommonAncestorOfClasses()
on the result.
public Set<Class<?>> getTargetTypes()
If there are zero reference fields in this path, then this method will return only the Java type of the starting object type, or possibly a sub-type if the target field exists only in a sub-type.
The returned type(s) will be maximally narrow. The set will contain only one element if a unique such type exists, otherwise it will contain multiple mutually incompatible supertypes of the object types at which this path ends.
public List<Set<Class<?>>> getPathTypes()
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 type(s) that are all assignable to the starting type
(possibly only the starting type if it was already maximally narrow), and the last element contains the
target type(s).
Each set in the returned list will be maximally narrow: it will contain only one element if a unique such type exists, otherwise it will contain multiple mutually incompatible supertypes of the object types at that step.
JClass
corresponding to each steppublic Set<TypeToken<?>> getTargetFieldTypes()
The returned type(s) will be maximally narrow. The set will contain only one element if a unique such type exists, otherwise it will contain multiple mutually incompatible supertypes of the object types at which this path ends. The latter case can only occur when the field is a reference field, and there are multiple Java model classes compatible with the field's type.
public int getTargetField()
This is just the storage ID of the last field in the path.
public int getTargetSuperField()
public int[] getReferenceFields()
Storage ID's will be negated to indicate reference fields traversed in the reverse direction.
The path may be empty, i.e., zero references are traversed in the path.
Otherwise, the first field is a field in the starting object type and the last field is field in some object type that refers to the target object type.
Copyright © 2022. All rights reserved.