JTimber/Parent Tracking

From LoadingByte Wiki
Jump to navigation Jump to search

Each tree node naturally tracks all other tree nodes which reference it through an attribute. Those referencing nodes are called parents. The runtime hook automatically inserts parent tracking hooks at runtime.

Activation

The parent node tracking can be activated for a certain class by implementing the interface com.quartercode.jtimber.api.node.ParentAware. As you can see, an object doesn't need to be an actual tree node to track its parents. However, all possible parents must implement the interface com.quartercode.jtimber.api.node.Node to mark them as being able to be a parent. That node interface is in turn parent-aware as well. That means that all nodes are also parent-aware, but not all parent-aware objects must be nodes.

It is really important to note that the node's attributes which store parent-aware objects should only be accessed from within the actual node class. Due to a technical limitation, only those changes are recorded. All other changes result in inconsistencies!

ParentAware

The ParentAware interface declares four methods, all of which need to be implemented. The contract for those methods can easily be derived from their names. However, the exact contract is defined in the JavaDoc. If you are able to, it is recommended to extend the class com.quartercode.jtimber.api.node.DefaultParentAware instead of implementing the ParentAware interface directly. That class already implements the four required methods.

public List<P> getParents()
public int getParentCount()

public void addParent(Node<?> parent)
public void removeParent(Node<?> parent)

Note that the two last methods are internal and should not be used by the average user. Only call them if you really know what you are doing!

The ParentAware interface also declares a generic parameter <P extends Node<?>>. It limits which nodes are able to be a parent of the parent-aware object. If a node tries to reference a parent-aware object and is not allowed to do that, a com.quartercode.jtimber.api.node.IllegalParentTypeException is thrown.

Node

The com.quartercode.jtimber.api.node.Node interface marks a class as being able to be a parent. However, it also declares two methods related to child tracking. You can either implement those methods yourself (as shown here) or just extend the class com.quartercode.jtimber.api.node.DefaultNode.

Wrappers

Wrappers are objects which wrap around other objects in order to provide more parent tracking functionality. For example, arrays and collections aren't supported natively. Wrappers help solve this problem in a very flexible and expandable way. You can read about them by clicking on the link provided earlier in this paragraph.

@Weak references

If you annotate a field inside a node class with the @com.quartercode.jtimber.api.node.Weak annotation, the parent relationship through that field is cut. That means that any parent-aware object stored in the field hasn't got the parent node, which holds the field, in its parent list. Note, however, that child tracking is not affected by the @Weak annotation. The node's getChildren() method still returns weakly referenced fields.

An example of a weak field:

@Weak
private Child weakRef = new Child();

If this code is part of your node class and Child is parent-aware, the weakRef child instance doesn't have your node class in its parent list. However, the node's getChildren() method still returns the weakRef child.

Retrieval

  • The parent-aware object's getParents() method returns a list containing all nodes which reference the parent-aware object through an attribute. If a parent node references the parent-aware object multiple times, the returned list contains that parent node multiple times as well.
  • The getParentCount() method returns the size of the list returned by the getParents() method. However, it is considerably faster.

The two internal methods addParent() and removeParent() just modify the list returned by getParents(). Those two methods are used by the bytecode added by the runtime hook and by wrappers.

Example

class Parent extends DefaultNode<Node<?>> {
    private Child child = new Child();

    // Getters and setters
}

class Child extends DefaultParentAware<Parent> {

}

public static void main(String[] args) {
    Parent parent = new Parent();
    Child child = parent.getChild();

    assert child.getParents().get(0) == parent;
}