Skip to content

Commit

Permalink
Generalized AbstractViewController
Browse files Browse the repository at this point in the history
The FlatViewController can be reused, but the target extraction
mechanism wasn't generic enough for this.

Other fixes :
- Node and Entry aggregation fixed a bit
- new Node aggregator which aggregates a node and its descendants into a
Flat aggregation
- TreeView is being prepared to show the aggregated flat view of a
selected node at the bottom
  • Loading branch information
PhRX committed Jan 26, 2017
1 parent cd2fb8f commit f5ce423
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 73 deletions.
@@ -0,0 +1,44 @@
package com.insightfullogic.honest_profiler.core.aggregation.aggregator;

import static java.util.stream.Collector.of;
import static java.util.stream.Collectors.groupingBy;

import java.util.ArrayList;
import java.util.List;

import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Entry;
import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Flat;
import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Node;
import com.insightfullogic.honest_profiler.core.profiles.lean.LeanNode;

/**
* Aggregator which takes a {@link Node}, and aggregates the values of the {@link Node} and its descendants into a list
* of {@link Entry}s.
*/
public class NodeDescendantAggregator implements Aggregator<Node<String>, String, Entry<String>>
{
/**
* This method aggregates a {@link Node} and all its all descendants.
*
* @see Aggregator#aggregate(Object, LeanNode)
*/
@Override
public Flat<String> aggregate(Node<String> parent, LeanNode reference)
{
List<Entry<String>> result = new ArrayList<>();
Flat<String> aggregation = new Flat<>(result, reference);

parent.flatten().collect(
groupingBy(
Node::getKey,
of(
// Supplier
() -> new Entry<String>(aggregation),
// Accumulator
(x, y) -> x.combine(y),
// Combiner
(x, y) -> x.combine(y))
));
return aggregation;
}
}
Expand Up @@ -19,7 +19,7 @@ public class Entry<K> implements Keyed<K>
private NumericInfo data; private NumericInfo data;
private List<LeanNode> aggregatedNodes; private List<LeanNode> aggregatedNodes;


protected <T extends Keyed<K>> Entry(Aggregation<K, T> aggregation) public <T extends Keyed<K>> Entry(Aggregation<K, T> aggregation)
{ {
this.data = new NumericInfo(); this.data = new NumericInfo();
this.aggregation = aggregation; this.aggregation = aggregation;
Expand Down Expand Up @@ -143,10 +143,11 @@ protected void copyInto(Entry<K> other)
other.aggregatedNodes = new ArrayList<>(aggregatedNodes); other.aggregatedNodes = new ArrayList<>(aggregatedNodes);
} }


public void combine(Entry<K> other) public Entry<K> combine(Entry<K> other)
{ {
aggregatedNodes.addAll(other.aggregatedNodes); aggregatedNodes.addAll(other.aggregatedNodes);
data.add(other.data); data.add(other.data);
return this;
} }


@Override @Override
Expand Down
Expand Up @@ -2,10 +2,15 @@


import static java.lang.Math.max; import static java.lang.Math.max;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
import static java.util.stream.Stream.concat;
import static java.util.stream.Stream.of;


import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream;


import com.insightfullogic.honest_profiler.core.aggregation.result.Aggregation; import com.insightfullogic.honest_profiler.core.aggregation.result.Aggregation;
import com.insightfullogic.honest_profiler.core.aggregation.result.Keyed; import com.insightfullogic.honest_profiler.core.aggregation.result.Keyed;
Expand All @@ -17,22 +22,22 @@
*/ */
public class Node<K> extends Entry<K> public class Node<K> extends Entry<K>
{ {
private List<Node<K>> children; private Map<K, Node<K>> children;


public <T extends Keyed<K>> Node(Aggregation<K, T> aggregation) public <T extends Keyed<K>> Node(Aggregation<K, T> aggregation)
{ {
super(aggregation); super(aggregation);
this.children = new ArrayList<>(); this.children = new HashMap<>();
} }


public <T extends Keyed<K>> Node(K key, NumericInfo data, Aggregation<K, T> aggregation) public <T extends Keyed<K>> Node(K key, NumericInfo data, Aggregation<K, T> aggregation)
{ {
super(key, data, aggregation); super(key, data, aggregation);
this.children = new ArrayList<>(); this.children = new HashMap<>();
} }


/** /**
* Copy constructor. * Constructor used for copying.
* *
* @param entry * @param entry
* @param children * @param children
Expand All @@ -41,12 +46,12 @@ private Node(Node<K> entry, List<Node<K>> children)
{ {
this(entry.getAggregation()); this(entry.getAggregation());
entry.copyInto(this); entry.copyInto(this);
this.children = children; children.forEach(child -> this.children.put(child.getKey(), child));
} }


public List<Node<K>> getChildren() public List<Node<K>> getChildren()
{ {
return children; return new ArrayList<>(children.values());
} }


@Override @Override
Expand All @@ -65,7 +70,7 @@ public int getDescendantDepth()
} }


int depth = 0; int depth = 0;
for (Node<K> child : children) for (Node<K> child : children.values())
{ {
depth = max(depth, child.getDescendantDepth() + 1); depth = max(depth, child.getDescendantDepth() + 1);
} }
Expand All @@ -81,14 +86,29 @@ public void add(K key, LeanNode node)
public Node<K> combine(Node<K> other) public Node<K> combine(Node<K> other)
{ {
super.combine(other); super.combine(other);
children.addAll(other.children); other.children.values().forEach(
child -> children
.compute(child.getKey(), (k, v) -> v == null ? child.copy() : v.combine(child)));
return this; return this;
} }


public Node<K> copyWithFilter(Predicate<Node<K>> filter) public Node<K> copy()
{ {
List<Node<K>> newChildren = children.stream().map(child -> child.copyWithFilter(filter)) List<Node<K>> newChildren = children.values().stream().map(child -> child.copy())
.filter(child -> child != null).collect(toList()); .filter(child -> child != null).collect(toList());
return new Node<>(this, newChildren);
}

public Node<K> copyWithFilter(Predicate<Node<K>> filter)
{
List<Node<K>> newChildren = children.values().stream()
.map(child -> child.copyWithFilter(filter)).filter(child -> child != null)
.collect(toList());
return newChildren.size() > 0 || filter.test(this) ? new Node<>(this, newChildren) : null; return newChildren.size() > 0 || filter.test(this) ? new Node<>(this, newChildren) : null;
} }

public Stream<Node<K>> flatten()
{
return concat(of(this), children.values().stream().flatMap(Node::flatten));
}
} }
Expand Up @@ -39,7 +39,7 @@
* *
* @param <T> the data type of the target * @param <T> the data type of the target
*/ */
public abstract class ProfileDiffViewController<T, U> extends AbstractViewController<U> public abstract class AbstractProfileDiffViewController<T, U> extends AbstractViewController<U>
{ {
private ProfileContext baseContext; private ProfileContext baseContext;
private ProfileContext newContext; private ProfileContext newContext;
Expand Down
@@ -1,23 +1,27 @@
package com.insightfullogic.honest_profiler.ports.javafx.controller; package com.insightfullogic.honest_profiler.ports.javafx.controller;


import static javafx.beans.binding.Bindings.createObjectBinding;

import java.util.function.Function; import java.util.function.Function;


import com.insightfullogic.honest_profiler.core.aggregation.result.ItemType; import com.insightfullogic.honest_profiler.core.aggregation.result.ItemType;
import com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext; import com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext;


import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;


/** /**
* Superclass for all View Controllers in the application which provide a view on the "target", a data structure of type * Superclass for all View Controllers in the application which provide a view on the "target", a data structure of type
* T which is stored in the {@link ProfileContext}. * T which is stored in the source object.
*
* This superclass also serves as a repository for the {@link ProfileContext}.
* *
* This superclass stores the {@link ProfileContext} encapsulating the target. It also ensures that subclass refresh() * It ensures that subclass refresh() implementations are called when a new instance of the target is available in the
* implementations are called when a new instance of the target is available in the context. * context.
* *
* This is achieved by keeping a local target {@link Property}, which can be bound or unbound to the target * This is achieved by keeping a local target {@link Property}, which can be bound or unbound to the target
* {@link Property} in the {@link ProfileContext}. * {@link Property} in the {@link ProfileContext}.
Expand All @@ -30,54 +34,36 @@
*/ */
public abstract class AbstractProfileViewController<T, U> extends AbstractViewController<U> public abstract class AbstractProfileViewController<T, U> extends AbstractViewController<U>
{ {
// Instance Properties


private ProfileContext profileContext; private ProfileContext profileContext;


private ObjectProperty<? extends Object> source;
private ObjectProperty<T> target; private ObjectProperty<T> target;
private Function<ProfileContext, ObservableValue<T>> targetExtractor;
private ObjectBinding<T> sourceToTargetBinding;

// Class Methods


/** /**
* This method must be called by subclasses in their FXML initialize(). It provides the extraction function which * This method must be called by subclasses in their FXML initialize(). It passes on the controller-local UI nodes
* specifies how to get the target from the {@link ProfileContext}. It also passes on the controller-local UI nodes
* needed by the AbstractViewController superclass. * needed by the AbstractViewController superclass.
* *
* @param targetExtractor function which extracts the target from the {@link ProfileContext}
* @param filterButton the button used to trigger filter editing * @param filterButton the button used to trigger filter editing
* @param quickFilterButton the button used to apply the quick filter * @param quickFilterButton the button used to apply the quick filter
* @param quickFilterText the TextField providing the value for the quick filter * @param quickFilterText the TextField providing the value for the quick filter
*/ */
protected void initialize(Function<ProfileContext, ObservableValue<T>> targetExtractor, @Override
Button filterButton, Button quickFilterButton, TextField quickFilterText, ItemType type) protected void initialize(Button filterButton, Button quickFilterButton,
TextField quickFilterText, ItemType type)
{ {
super.initialize(filterButton, quickFilterButton, quickFilterText, type); super.initialize(filterButton, quickFilterButton, quickFilterText, type);


this.targetExtractor = targetExtractor;
target = new SimpleObjectProperty<>(); target = new SimpleObjectProperty<>();
target.addListener((property, oldValue, newValue) -> refresh()); target.addListener((property, oldValue, newValue) -> refresh());
} }


// Activation // Instance Accessors

/**
* Binds the local target {@link Property} to the target {@link Property} in the {@link ProfileContext}, using the
* target extractor function. The net effect is that the controller will start tracking changes to the target
* instance in the {@link ProfileContext}.
*/
public void activate()
{
target.bind(targetExtractor.apply(profileContext));
}

/**
* Unbinds the local target {@link Property}. The controller no longer tracks changes to the target {@link Property}
* in the {@link ProfileContext}.
*/
public void deactivate()
{
target.unbind();
}

// Accessors


/** /**
* Returns the {@link ProfileContext}. The name has been shortened to unclutter code in subclasses. * Returns the {@link ProfileContext}. The name has been shortened to unclutter code in subclasses.
Expand Down Expand Up @@ -109,6 +95,49 @@ protected T getTarget()
return target.get(); return target.get();
} }


/**
* Set the source object the target data structure T will be extracted from.
*
* @param source the source providing the target data structure
*/
public void setSource(ObjectProperty<? extends Object> source)
{
this.source = source;
}

/**
* Set the function which extracts the target data structure T from the source object.
*
* @param targetExtractor a function which extracts the target from the source object
*/
public void setTargetExtractor(Function<Object, T> targetExtractor)
{
sourceToTargetBinding = createObjectBinding(
() -> targetExtractor.apply(source.get()),
source);
}

// Activation

/**
* Binds the local target {@link Property} to the target {@link Property} in the {@link ProfileContext}, using the
* target extractor function. The net effect is that the controller will start tracking changes to the target
* instance in the {@link ProfileContext}.
*/
public void activate()
{
target.bind(sourceToTargetBinding);
}

/**
* Unbinds the local target {@link Property}. The controller no longer tracks changes to the target {@link Property}
* in the {@link ProfileContext}.
*/
public void deactivate()
{
target.unbind();
}

// AbstractViewController Implementation // AbstractViewController Implementation


/** /**
Expand Down
Expand Up @@ -39,7 +39,6 @@ public class FlameViewController extends AbstractProfileViewController<FlameGrap
protected void initialize() protected void initialize()
{ {
super.initialize( super.initialize(
profileContext -> profileContext.flameGraphProperty(),
null, null,
null, null,
null, null,
Expand All @@ -48,6 +47,14 @@ protected void initialize()
rootContainer.getChildren().add(flameView); rootContainer.getChildren().add(flameView);
} }


@Override
public void setProfileContext(ProfileContext profileContext)
{
super.setProfileContext(profileContext);
super.setSource(profileContext.flameGraphProperty());
super.setTargetExtractor(flameGraph -> (FlameGraph)flameGraph);
}

public void refreshFlameView() public void refreshFlameView()
{ {
if (!flameView.widthProperty().isBound()) if (!flameView.widthProperty().isBound())
Expand Down
Expand Up @@ -58,7 +58,7 @@
import javafx.scene.control.TextField; import javafx.scene.control.TextField;


public class FlatDiffViewController public class FlatDiffViewController
extends ProfileDiffViewController<AggregationProfile, DiffEntry<String>> extends AbstractProfileDiffViewController<AggregationProfile, DiffEntry<String>>
{ {
@FXML @FXML
private Button filterButton; private Button filterButton;
Expand Down
Expand Up @@ -39,6 +39,7 @@


import com.insightfullogic.honest_profiler.core.aggregation.AggregationProfile; import com.insightfullogic.honest_profiler.core.aggregation.AggregationProfile;
import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Entry; import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Entry;
import com.insightfullogic.honest_profiler.core.aggregation.result.straight.Flat;
import com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext; import com.insightfullogic.honest_profiler.ports.javafx.model.ProfileContext;
import com.insightfullogic.honest_profiler.ports.javafx.util.report.ReportUtil; import com.insightfullogic.honest_profiler.ports.javafx.util.report.ReportUtil;
import com.insightfullogic.honest_profiler.ports.javafx.view.cell.GraphicalShareTableCell; import com.insightfullogic.honest_profiler.ports.javafx.view.cell.GraphicalShareTableCell;
Expand All @@ -52,8 +53,7 @@
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.control.cell.PropertyValueFactory;


public class FlatViewController public class FlatViewController extends AbstractProfileViewController<Flat<String>, Entry<String>>
extends AbstractProfileViewController<AggregationProfile, Entry<String>>
{ {
@FXML @FXML
private Button filterButton; private Button filterButton;
Expand Down Expand Up @@ -93,7 +93,6 @@ public class FlatViewController
protected void initialize() protected void initialize()
{ {
super.initialize( super.initialize(
profileContext -> profileContext.profileProperty(),
filterButton, filterButton,
quickFilterButton, quickFilterButton,
quickFilterText, quickFilterText,
Expand All @@ -112,6 +111,8 @@ public void setProfileContext(ProfileContext profileContext)
flatProfileView.getColumns().forEach(column -> column.setSortable(false)); flatProfileView.getColumns().forEach(column -> column.setSortable(false));
} }
super.setProfileContext(profileContext); super.setProfileContext(profileContext);
super.setSource(profileContext.profileProperty());
super.setTargetExtractor(o -> o == null ? null : ((AggregationProfile)o).getFlat());
} }


// Initialization Helper Methods // Initialization Helper Methods
Expand Down Expand Up @@ -164,7 +165,7 @@ protected void initializeHandlers()
protected void refresh() protected void refresh()
{ {
flatProfile.clear(); flatProfile.clear();
flatProfile.addAll(getTarget().getFlat().filter(getFilterSpecification()).getData()); flatProfile.addAll(getTarget().filter(getFilterSpecification()).getData());
flatProfileView.refresh(); flatProfileView.refresh();
refreshTable(flatProfileView); refreshTable(flatProfileView);
} }
Expand Down

0 comments on commit f5ce423

Please sign in to comment.