|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||
public interface Forest
Forest interface provides access to a data structure that stores issue hierarchy
(the hierarchy is a sequence of tree structures, a forest).
Forest lets you read and modify the data stored in the forest data structure.
Difference between Structure and Forest
interfaces: Structure exposes properties of a structure such as name, and also lets you
modify those properties and save them back to the database; while Forest is the hierarchy
itself, it's just a data structure. Any modifications to the Forest are only applied to
that instance in memory.
Forest of issues is represented as a sequence of pairs (long issue, int depth). The issues
are identified by their issue ID, and the position in the hierarchy is identified by the position
in the sequence and the associated depth. For example, the following hierarchy:
A
A1
A2
B
B1
B1X
is represented in the forest as the following sequence:
(A, 0), (A1, 1), (A2, 1), (B, 0), (B1, 1), (B1X, 2)
Forest interface provides indexed access to that representation of the forest.
That is, you can read issue IDs and depths given that you know the index of the pair in the
sequence. There are also a number of utility methods that allow searching and browsing the
forest.
Because depth associated with an issue is an integer number, there are certain
invariants about the depth that must hold true, otherwise the data does not make sense. Additionally,
an issue may appear no more than once in the forest. Make sure
you don't violate those invariants when creating new forest instances:
0.D,
the depth of the following element must be within range [0 .. D+1].
N, it must not be present
at any other index.0 - this value represents "missing" or "no value" in various methods of this interface.When Java assertions are enabled, these invariants are checked all the time. Due to performance impact, the invariants are not checked and assumed to hold true when running with assertions disabled. The result of creating and using a forest that violates these invariants is undefined.
When a specific position in the forest needs to be defined for mutating operations such as move or insert, under-after
coordinates are used. (If operation addresses a specific issue, the issue ID is used instead.)
Under-after coordinates contain under value, which identifies the parent issue of the
specified position (the depth and the subtree). If under is 0, the position is at the
top (root issue).
The other parameter, after coordinate, defines the location of the inserted issue amongst siblings -
the issues at the same depth under the same parent. If after is 0, it means that the position
should be the first under the specified parent.
Note: moving an issue after itself, i.e. when the under coordinate is equal to the issue's parent issue
and the after coordinate is equal to the issue itself, is allowed and does not change the position of the issue.
This class in not thread-safe.
| Method Summary | ||
|---|---|---|
boolean |
addIssue(long issue,
long under,
long after)
Adds a single issue at the specified position. |
|
boolean |
containsIssue(long issue)
Can be used to check if the forest contains a specific issue. |
|
Forest |
copy()
Creates an exact copy of this forest. |
|
Forest |
copySubtree(long issue)
Creates a forest that contains the specified issue and all its sub-issues from this forest. |
|
Forest |
copySubtreeAtIndex(int index)
Creates a forest that contains the specified issue and all its sub-issues from this forest. |
|
Forest |
filter(La<Long,?> filter)
Filters this forest by hiding issues that do not pass the filter condition. |
|
Forest |
filterForIssue(long issue)
Creates a sub-sequence forest of this forest that contains all issues "relevant" to the passed issue. |
|
Forest |
filterSoft(La<Long,?> filter)
Filters this forest by excluding issues that do not pass the filter condition. |
|
|
foldUpwards(ForestParentChildrenClosure<T,C> closure)
This is a more generic version of visitParentChildrenUpwards(com.almworks.jira.structure.api.forest.ForestParentChildrenVisitor) that allows you to effectively
process the forest bottom-up, probably saving on memory allocation and search speed. |
|
com.almworks.integers.LongArray |
getChildren(long issue)
Creates an array with all direct sub-issues of the specified issue. |
|
com.almworks.integers.LongArray |
getChildrenAtIndex(int index)
Creates an array with all direct sub-issues of the issue at the specified index. |
|
int |
getDepth(int index)
Gets the depth of an issue at the specified position in the forest. |
|
com.almworks.integers.IntList |
getDepths()
|
|
long |
getIssue(int index)
Gets the ID of an issue at the specified position in the forest. |
|
com.almworks.integers.LongList |
getIssues()
|
|
long |
getLastChild(long parent)
Gets the last direct sub-issue of the specified parent issue. |
|
long |
getLastChildByIndex(int parentIndex)
Gets the last direct sub-issue of the specified parent issue. |
|
long |
getParent(long issue)
Gets the parent issue of the specified issue. |
|
int |
getParentIndex(int index)
Searches the forest for the index of a "parent issue". |
|
com.almworks.integers.LongArray |
getParentPathForIndex(int index)
Returns the path to the specified issue without the issue itself. |
|
com.almworks.integers.LongArray |
getPath(long issue)
Returns the path to the specified issue - a sequence of issues that starts with a root issue, ends with the specified issue, and where element[i] is
the parent issue of element[i+1]. |
|
com.almworks.integers.LongArray |
getPathForIndex(int index)
Similar to getPath(long), returns the path to the issue specified by index. |
|
int |
getPathIndexAtDepth(int index,
int depth)
Given issue at the specified index, traverse its "path" upwards - that is, look for all parent issues up to the topmost root parent, and return an index of the parent that has the specified depth. |
|
long |
getPrecedingSibling(long issue)
Gets the issue that immediately precedes the specified issue in the list of children of the specified issue's parent. |
|
long |
getPrecedingSiblingForIndex(int index)
Gets the issue that immediately precedes the one with the given index in the list of children of its parent. |
|
int |
getPrecedingSiblingIndex(int index)
Gets the index of an issue that immediately precedes the issue at the given index in the list of children of its parent. |
|
com.almworks.integers.LongArray |
getPrecedingSiblings(long issue)
Returns the array of all issues that come before the given issue in the list of children of its parent, in the same order as they appear in the forest. |
|
com.almworks.integers.LongArray |
getPrecedingSiblingsForIndex(int index)
Returns the array of all issues that come before the given issue in the list of children of its parent, in the same order as they appear in the forest. |
|
com.almworks.integers.LongArray |
getRoots()
|
|
int |
getSubtreeEnd(int index)
The method looks for the end of a subtree rooted at the specified index. |
|
int |
indexOf(long issue)
Searches for the position of a specific issue in the forest. |
|
boolean |
isEmpty()
|
|
Forest |
makeImmutable()
Makes this forest immutable - all mutator operations will throw UnsupportedOperationException. |
|
void |
mergeForest(Forest forest,
long under,
long after)
Convenience method to call mergeForest(Forest, long, long, ForestChangeEventHandler) without event handler. |
|
void |
mergeForest(Forest forest,
long under,
long after,
ForestChangeEventHandler eventHandler)
Merges another forest into this forest. |
|
boolean |
moveSubtree(long issue,
long under,
long after)
Convenience method to call moveSubtree(long, long, long, ForestChangeEventHandler) without event handler. |
|
boolean |
moveSubtree(long issue,
long under,
long after,
ForestChangeEventHandler eventHandler)
Moves sub-tree rooted at the specified issue to a position specified by (under, after) coordinates. |
|
int |
moveSubtreeAtIndex(int index,
long under,
long after,
ForestChangeEventHandler eventHandler)
Moves sub-tree rooted at the specified index to a position specified by (under, after) coordinates. |
|
Forest |
removeSubtree(long issue)
Convenience method to call removeSubtree(long, ForestChangeEventHandler) without event handler. |
|
Forest |
removeSubtree(long issue,
ForestChangeEventHandler eventHandler)
Removes a sub-tree with the specified issue as a root from this forest. |
|
Forest |
removeSubtreeAtIndex(int index,
ForestChangeEventHandler eventHandler)
Removes a sub-tree with rooted at the specified index from this forest. |
|
Forest |
set(com.almworks.integers.LongList issues,
com.almworks.integers.IntList depths)
Replaces the contents of this forest with the values passed in the parameters. |
|
Forest |
set(com.almworks.integers.WritableLongList issues,
com.almworks.integers.WritableIntList depths,
boolean reuseLists)
Replaces the contents of this forest with the values passed in the parameters. |
|
int |
size()
|
|
String |
toFullString()
Utility method for debugging - returns full string representation of the forest, that contains all the information, unlike toString method, which may be truncated to some character number limit. |
|
void |
visitParentChildrenUpwards(ForestParentChildrenVisitor visitor)
This method is used to efficiently traverse all pairs (parent, children) from the end of the forest upwards. |
|
| Method Detail |
|---|
int size()
boolean isEmpty()
@NotNull com.almworks.integers.LongList getIssues()
size().@NotNull com.almworks.integers.IntList getDepths()
size() and the i-th element
of this list corresponds to the issue ID at the i-th position in the list returned
by getIssues().long getIssue(int index)
index - the index of the forest entry
IndexOutOfBoundsException - if index is not within range [0 .. size() - 1].int getDepth(int index)
index - the index of the forest entry
Forest
IndexOutOfBoundsException - if index is not within range [0 .. size() - 1].int indexOf(long issue)
Searches for the position of a specific issue in the forest.
Execution of this method may take O(size()) time, however it may be
optimized if the implementation maintains an index. It's better to use this method rather
than using forest.getIssues().indexOf(issue) because of the possible optimizations.
issue - the issue ID to search for
-1 if the issue is not foundboolean containsIssue(long issue)
Can be used to check if the forest contains a specific issue.
Execution of this method may take O(size()) time.
issue - the issue ID to search for
int getSubtreeEnd(int index)
The method looks for the end of a subtree rooted at the specified index.
A subtree rooted at index k is a sub-sequence in the forest starting at position k
and containing all following elements that have depth d > depth[k].
The result of this method is the next index after the last element of this subsequence.
More specifically, the result is the first element after [k] that has depth d <= depth[k].
When the subtree ends with the whole forest, the return value is equal to size().
If index is a negative number, the returned value is 0. If index is greater or equal than
size(), the returned value is equal to size().
index - the index of the root issue of the subtree
int getParentIndex(int index)
Searches the forest for the index of a "parent issue". If the issue at the specified index is a root issue (has depth of 0),
returns -1.
If index is negative, returns -1.
index - index of a child issue
index is a root issue, or index k of the parent issue, such as
that k = MAX(k in [0, index-1] that has depth[k] == depth[index] - 1)
IndexOutOfBoundsException - if index is equal or greater than forest sizelong getParent(long issue)
issue - child issue
0 if the child issue is a root issue or not found in the forestlong getPrecedingSibling(long issue)
issue - an issue
issue is a root issue or not found in the forest or is the first child of its parent issueint getPrecedingSiblingIndex(int index)
index - issue index
index is a root issue or not found in the forest or is the first child of its parent issuelong getPrecedingSiblingForIndex(int index)
index - issue index
@NotNull com.almworks.integers.LongArray getPrecedingSiblings(long issue)
issue - an issue
issue has none or is not in the forest@NotNull com.almworks.integers.LongArray getPrecedingSiblingsForIndex(int index)
index - issue index
index has none or index is negative
int getPathIndexAtDepth(int index,
int depth)
Given issue at the specified index, traverse its "path" upwards - that is, look for all parent issues up to the topmost root parent, and return an index of the parent that has the specified depth.
If the required depth is equal to the depth of the issue at the specified index, returns index.
If index is a negative value, returns -1. If issue at the specified index has less depth than the required value, returns -1.
index - index of an issuedepth - required depth of the [grand-] parent issue
-1 if not found
IndexOutOfBoundsException - if index is equal or greater than the size of the forestlong getLastChild(long parent)
Gets the last direct sub-issue of the specified parent issue.
Special case: when parent is 0,
returns the last root issue in the forest.
If the parent issue is not in the forest, or if it does not have child issues, the return value is 0.
parent - parent issue
long getLastChildByIndex(int parentIndex)
Gets the last direct sub-issue of the specified parent issue.
Special case: when parentIndex is less than zero,
returns the last root issue in the forest.
If the parent issue does not have child issues, the return value is 0.
parentIndex - the index of the parent issue
@NotNull com.almworks.integers.LongArray getChildren(long issue)
Creates an array with all direct sub-issues of the specified issue.
The returned array is writable and owned by the calling code.
If issue is not in the forest or does not have children, empty array is returned.
issue - the parent issue
@NotNull com.almworks.integers.LongArray getChildrenAtIndex(int index)
Creates an array with all direct sub-issues of the issue at the specified index.
The returned array is writable and owned by the calling code.
If the specified issue does not have children, or if the index is negative, empty array is returned.
index - the index of the parent issue
IndexOutOfBoundsException - if the index is greater or equals the size of the forest@NotNull com.almworks.integers.LongArray getRoots()
0)@NotNull com.almworks.integers.LongArray getPath(long issue)
Returns the path to the specified issue - a sequence of issues that starts with a
root issue, ends with the specified issue, and where element[i] is
the parent issue of element[i+1].
If issue is not in the forest, returns empty array.
The array is modifiable and owned by the calling code.
issue - issue to get the path to
@NotNull com.almworks.integers.LongArray getPathForIndex(int index)
getPath(long), returns the path to the issue specified by index.
index - the index of an issue to get the path to
NoSuchElementException - if the index is greater than or equal to forest size@NotNull com.almworks.integers.LongArray getParentPathForIndex(int index)
Returns the path to the specified issue without the issue itself. In other words, this is the path to the parent of the specified issue, if there is one.
index - issue index
getPath(long)
@NotNull
Forest filter(@Nullable
La<Long,?> filter)
Filters this forest by hiding issues that do not pass the filter condition. The
resulting forest contains only the issues that pass the condition.
The topology of the resulting forest may differ from the original forest - that is, an issue may
have a different parent in the resulting forest. This happens when an issue that has sub-issues is filtered
out - in that case, its sub-tree is substituted instead of the parent issue.
This is different from filterSoft(com.almworks.jira.structure.util.La method, which preserves the topology.
This forest is not modified by this method. If all issues pass the condition, then this forest is returned as the result. If filtering has taken place, a new forest is returned.
The filter method is called once for every issue in the forest, and a truthy result
(as defined in La.accepts(T)) indicates that the issue passes the filter.
filter - filter that returns a truthy value if an issue with given ID is allowed to be present in the resulting forest
filterSoft(com.almworks.jira.structure.util.La)
@NotNull
Forest filterSoft(@Nullable
La<Long,?> filter)
Filters this forest by excluding issues that do not pass the filter condition. All issues
that contain sub-issues that have passed the filter are also preserved. The
resulting forest contains sub-sequence of the original forest with issues having the same parents and depths.
Unlike filter(com.almworks.jira.structure.util.La method, this method preserves the topology of the original forest -
all issues in the resulting forest have the same root path as they do in the original forest.
This forest is not modified by this method. If all issues pass the condition, then this forest is returned as the result. If filtering has taken place, a new forest is returned.
The filter method is called once for every issue in the forest, and a truthy result
(as defined in La.accepts(T)) indicates that the issue passes the filter.
filter - filter that returns a truthy value if an issue with given ID should be present in the resulting forest
@NotNull Forest filterForIssue(long issue)
Creates a sub-sequence forest of this forest that contains all issues "relevant" to the passed issue.
The resulting forest contains all the parent issues of issue up to the root issue, and
all the sub-issues of issue down to the deepest level.
This filter preserves the topology of the forest - all issues in the resulting forest have the same parents as in the original forest.
If issue is not in this forest, returns empty forest.
Always returns a new instance.
issue - the issue
@NotNull Forest copy()
@NotNull Forest copySubtree(long issue)
issue - the root of the sub-tree
issue, or an empty forest if the issue is not in this forest@NotNull Forest copySubtreeAtIndex(int index)
index - index of the root of the sub-tree
index, or an empty forest if the index is negative
boolean moveSubtree(long issue,
long under,
long after,
@Nullable
ForestChangeEventHandler eventHandler)
throws StructureException
Moves sub-tree rooted at the specified issue to a position specified by (under, after) coordinates.
This method modifies the forest by removing the sub-tree with the specified issue as the root and adding it
at the position specified by under-after coordinates. See Forest for the explanation
of under-after coordinates.
This method does not modify the forest stored in the database for a structure.
To make modifications of the structure's forest, run forest changing transaction with StructureManager.updateForest(com.atlassian.crowd.embedded.api.User, boolean, com.almworks.jira.structure.api.forest.ForestTransaction.
issue - the root issue of the sub-tree being movedunder - the new parent of the sub-tree, or 0 if the sub-tree should be placed at the forest root levelafter - the preceding sibling of the new location for the sub-tree, or 0 to place sub-tree as the first childeventHandler - optional handler of the move events
StructureException - if the move is not possible - for example, under is not in the forest or if you attempt to move a sub-tree under itself
boolean moveSubtree(long issue,
long under,
long after)
throws StructureException
moveSubtree(long, long, long, ForestChangeEventHandler) without event handler.
issue - the root issue of the sub-tree being movedunder - the new parent of the sub-tree, or 0 if the sub-tree should be placed at the forest root levelafter - the preceding sibling of the new location for the sub-tree, or 0 to place sub-tree as the first child
StructureException - if the move is not possible - for example, under is not in the forest or if you attempt to move a sub-tree under itself
int moveSubtreeAtIndex(int index,
long under,
long after,
@Nullable
ForestChangeEventHandler eventHandler)
throws StructureException
Moves sub-tree rooted at the specified index to a position specified by (under, after) coordinates.
This method modifies the forest by removing the sub-tree with the specified issue as the root and adding it
at the position specified by under-after coordinates. See Forest for the explanation
of under-after coordinates.
This method does not modify the forest stored in the database for a structure.
To make modifications of the structure's forest, run forest changing transaction with StructureManager.updateForest(com.atlassian.crowd.embedded.api.User, boolean, com.almworks.jira.structure.api.forest.ForestTransaction.
index - the index of the root issue of the sub-tree being movedunder - the new parent of the sub-tree, or 0 if the sub-tree should be placed at the forest root levelafter - the preceding sibling of the new location for the sub-tree, or 0 to place sub-tree as the first childeventHandler - optional handler of the move events
StructureException - if the move is not possible - for example, under is not in the forest or if you attempt to move a sub-tree under itself
@NotNull
Forest removeSubtree(long issue,
@Nullable
ForestChangeEventHandler eventHandler)
Removes a sub-tree with the specified issue as a root from this forest.
This method does not modify the forest stored in the database for a structure.
To make modifications of the structure's forest, run forest changing transaction with StructureManager.updateForest(com.atlassian.crowd.embedded.api.User, boolean, com.almworks.jira.structure.api.forest.ForestTransaction.
issue - the root of the sub-tree to be removedeventHandler - optional handler of the forest events
issue is not in this forest@NotNull Forest removeSubtree(long issue)
removeSubtree(long, ForestChangeEventHandler) without event handler.
issue - the root of the sub-tree to be removed
issue is not in this forest.
@NotNull
Forest removeSubtreeAtIndex(int index,
@Nullable
ForestChangeEventHandler eventHandler)
Removes a sub-tree with rooted at the specified index from this forest.
This method does not modify the forest stored in the database for a structure.
To make modifications of the structure's forest, run forest changing transaction with StructureManager.updateForest(com.atlassian.crowd.embedded.api.User, boolean, com.almworks.jira.structure.api.forest.ForestTransaction.
index - the index of the root of the sub-tree to be removedeventHandler - optional handler of the forest events
index is negative
boolean addIssue(long issue,
long under,
long after)
throws StructureException
Adds a single issue at the specified position.
If the issue is already in the structure, this method moves it to the specified position.
This method does not modify the forest stored in the database for a structure.
To make modifications of the structure's forest, run forest changing transaction with StructureManager.updateForest(com.atlassian.crowd.embedded.api.User, boolean, com.almworks.jira.structure.api.forest.ForestTransaction.
issue - issue to be addedunder - the parent of the issue, or 0 if the issue should be placed at the forest root levelafter - the preceding sibling of the issue, or 0 to place issue as the first child
StructureException - if under is not in the forest or if a similar problem happens
void mergeForest(@Nullable
Forest forest,
long under,
long after,
@Nullable
ForestChangeEventHandler eventHandler)
throws StructureException
Merges another forest into this forest. After the method has executed, this forest will contain all issues
from forest in the same topology under the specified positions: the root issues from forest
will be positioned as defined by under-after coordinates (see Forest for the explanation
of under-after coordinates), and non-root issues from forest will have the same parent issues as
in forest.
This forest and the merged forest may have the same issues. In that case the issues are moved
within this forest and placed at the position required by this operation.
This method does not modify the forest stored in the database for a structure.
To make modifications of the structure's forest, run forest changing transaction with StructureManager.updateForest(com.atlassian.crowd.embedded.api.User, boolean, com.almworks.jira.structure.api.forest.ForestTransaction.
When StructureException is thrown from this method, the state of this forest is unknown - due to the
fact that merge splits into several atomic updates and the exception may be thrown after some of the updates
have taken place.
forest - the merged forestunder - the parent of the merged forest, or 0 if the forest issues should be placed at the root levelafter - the preceding sibling of the merged forest, or 0 to place forest as the first childeventHandler - an optional event handler to get notifications about changes being applied - may be called several times
because merge could be split into series of moves and additions.
StructureException - if the operation cannot be completed because it requires an invalid move
void mergeForest(@Nullable
Forest forest,
long under,
long after)
throws StructureException
mergeForest(Forest, long, long, ForestChangeEventHandler) without event handler.
forest - the merged forestunder - the parent of the merged forest, or 0 if the forest issues should be placed at the root levelafter - the preceding sibling of the merged forest, or 0 to place forest as the first child
StructureException - if the operation cannot be completed because it requires an invalid move
Forest set(com.almworks.integers.WritableLongList issues,
com.almworks.integers.WritableIntList depths,
boolean reuseLists)
Replaces the contents of this forest with the values passed in the parameters.
The passed lists must satisfy the invariant conditions listed in Forest, otherwise the results
are undefined.
This method does not modify the forest stored in the database for a structure.
To make modifications of the structure's forest, run forest changing transaction with StructureManager.updateForest(com.atlassian.crowd.embedded.api.User, boolean, com.almworks.jira.structure.api.forest.ForestTransaction.
issues - new issue listdepths - new depths listreuseLists - if true, the passed arrays may be used by the forest without copying the data. In that case, the calling code must not use these lists after this method call.
Forest set(com.almworks.integers.LongList issues,
com.almworks.integers.IntList depths)
Replaces the contents of this forest with the values passed in the parameters.
The passed lists must satisfy the invariant conditions listed in Forest, otherwise the results
are undefined.
This method does not modify the forest stored in the database for a structure.
To make modifications of the structure's forest, run forest changing transaction with StructureManager.updateForest(com.atlassian.crowd.embedded.api.User, boolean, com.almworks.jira.structure.api.forest.ForestTransaction.
issues - new issue listdepths - new depths list
void visitParentChildrenUpwards(ForestParentChildrenVisitor visitor)
This method is used to efficiently traverse all pairs (parent, children) from the end of the forest upwards.
This method goes over the forest in the backwards direction and reports to the visitor pairs of (parent, direct children).
Invariants:
If the forest is modified during iteration, the results are undefined.
visitor - an instance that receives the pairs of parent and children array@Nullable <T,C> C foldUpwards(ForestParentChildrenClosure<T,C> closure)
This is a more generic version of visitParentChildrenUpwards(com.almworks.jira.structure.api.forest.ForestParentChildrenVisitor) that allows you to effectively
process the forest bottom-up, probably saving on memory allocation and search speed.
The method goes over the forest in the backwards direction and calls closure methods for each
issue:
ForestParentChildrenClosure.visitIssue(com.almworks.jira.structure.api.forest.ForestFoldControl, long, com.almworks.integers.LongList, C) is called for every issue (in the same way visitParentChildrenUpwards(com.almworks.jira.structure.api.forest.ForestParentChildrenVisitor))
works;visitIssue() can return a result of the processing;ForestParentChildrenClosure.combine(com.almworks.jira.structure.api.forest.ForestFoldControl, T, C) is called after each call to visitIssue() to aggregate the results
of children under the same parent.
T - the type of the result of processing one issueC - the type of the result of processing a number of sub-issues under the same parentclosure - the closure
visitParentChildrenUpwards(com.almworks.jira.structure.api.forest.ForestParentChildrenVisitor),
ForestParentChildrenClosure@NotNull Forest makeImmutable()
UnsupportedOperationException.
@NotNull String toFullString()
toString method, which may be truncated to some character number limit.
|
||||||||||
| PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
| SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD | |||||||||