/* ===================================================================== * Ocelotl Visualization Tool * ===================================================================== * * Ocelotl is a Framesoc plug in that enables to visualize a trace * overview by using aggregation techniques * * (C) Copyright 2013 INRIA * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Damien Dosimont <damien.dosimont@imag.fr> * Generoso Pagano <generoso.pagano@inria.fr> */ package fr.inria.soctrace.tools.ocelotl.core.dataaggregmanager.spacetime; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import fr.inria.soctrace.framesoc.ui.utils.AlphanumComparator; import fr.inria.soctrace.lib.model.EventProducer; import fr.inria.soctrace.tools.ocelotl.core.exceptions.OcelotlException; import fr.inria.soctrace.tools.ocelotl.core.microdesc.Microscopic3DDescription; public class EventProducerHierarchy { private static final Logger logger = LoggerFactory.getLogger(EventProducerHierarchy.class); public enum Aggregation { FULL, PARTIAL, NULL } public class EventProducerNode { private int id; private EventProducer me; private EventProducerNode parentNode; private List<EventProducerNode> childrenNodes = new ArrayList<EventProducerNode>(); private List<Integer> parts; // Number of leaf event producers in the node private int weight = 1; private Aggregation aggregated = Aggregation.NULL; private Object values; private int index; // Depth in the hierarchy level of the node (the smaller, the higher // in the hierarchy) private int hierarchyLevel; private boolean realLeaf = true; public EventProducerNode(EventProducer ep) { if(ep == null) throw new NullPointerException(); me = ep; id = me.getId(); orphans.put(id, this); leaves.put(id, this); hierarchyLevel = 0; setParent(); } /** * Constructor for false leaf (i.e. for producers that have events but are not leaves * @param ep * @param epn */ public EventProducerNode(EventProducer ep, EventProducerNode epn) { me = ep; // False ID (must be unique to avoid problem at later stages) id = me.getId() + maxID + 1; hierarchyLevel = epn.getHierarchyLevel() + 1; parentNode = epn; // Update the weight of the parent parentNode.weight = parentNode.getWeight(); epn.getChildrenNodes().add(this); realLeaf = false; } public Aggregation isAggregated() { return aggregated; } public int getWeight() { return weight; } public List<Integer> getParts() { return parts; } public void setParts(List<Integer> parts) { this.parts = parts; if (!parts.contains(-1)) aggregated = Aggregation.FULL; else { aggregated = Aggregation.NULL; for (int part : parts) { if (part != -1) { aggregated = Aggregation.PARTIAL; } } } } public int getID() { return id; } private void setParent() { try { if (!eventProducerNodes.containsKey(me.getParentId())) { // Is the parent id is a known producer if (eventProducers.containsKey(me.getParentId())) { eventProducerNodes.put( me.getParentId(), new EventProducerNode(eventProducers.get(me .getParentId()))); } else { // If not make it root (which has no parent) parentNode = null; if (root == null) { root = this; orphans.remove(id); } return; } } parentNode = eventProducerNodes.get(me.getParentId()); parentNode.addChild(this); hierarchyLevel = parentNode.getHierarchyLevel() + 1; if(hierarchyLevel > maxHierarchyLevel) maxHierarchyLevel = hierarchyLevel; orphans.remove(id); } catch (NullPointerException e) { parentNode = null; if (root == null) { root = this; orphans.remove(id); } } } public void addChild(EventProducerNode child) { childrenNodes.add(child); if (leaves.containsKey(id)) { leaves.remove(id); realLeaf = false; } } public EventProducer getMe() { return me; } public EventProducerNode getParentNode() { return parentNode; } public List<EventProducerNode> getChildrenNodes() { return childrenNodes; } /** * Sort children nodes alphabetically */ public void sortChildrenNodes() { Collections.sort(childrenNodes, new Comparator<EventProducerNode>() { @Override public int compare(EventProducerNode o1, EventProducerNode o2) { return AlphanumComparator.compare(o1.getMe() .getName(), o2.getMe().getName()); } }); } public void destroy() { for (EventProducerNode child : childrenNodes) { child.destroy(); } eventProducerNodes.remove(id); if (orphans.containsKey(id)) orphans.remove(id); if (leaves.containsKey(id)) leaves.remove(id); eventProducers.remove(id); childrenNodes.clear(); } public Object getValues() { return values; } public void setValues(Object values) { // if (leaves.containsKey(id) || pseudoLeaves.containsKey(id)) this.values = values; // else // values = null; } public void setParentValues(Object values) { if (!leaves.containsKey(id)) this.values = values; else values = null; } /** * Compute the weight (number of leaves in the node) for the node and * recursively for all its children * * @return the newly computed weight */ public int setWeight() { if (childrenNodes.isEmpty()) { // If it is an aggregated leave if (microModel3D.getAggregatedProducers() .containsValue(me)) { weight = microModel3D .removeFilteredEP( microModel3D .getOcelotlParameters() .getEventProducerHierarchy() .getLeaves( microModel3D .getOcelotlParameters() .getEventProducerHierarchy() .getEventProducerNodes() .get(this.getMe() .getId()))) .size(); aggLeaves.add(this); } return weight; } else weight = 0; for (EventProducerNode epn : childrenNodes) { weight += epn.setWeight(); } return weight; } /** * Recursively compute the index of the node based on the sum of the * weights of the previous children so that each node indicates the * previous leaves in the sorting order (currently alphabetical) */ public void setChildIndex() { if (this == root) { index = 0; } sortChildrenNodes(); int currentweight = 0; for (EventProducerNode e : childrenNodes) { e.setIndex(currentweight + index); e.setChildIndex(); currentweight += e.getWeight(); } } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public int getHierarchyLevel() { return hierarchyLevel; } public void setHierarchyLevel(int hierarchyLevel) { this.hierarchyLevel = hierarchyLevel; } public boolean isRealLeaf() { return realLeaf; } public void setRealLeaf(boolean realLeaf) { this.realLeaf = realLeaf; } /** * Check whether or not the current epn contain another epn given as parameter * * @param anEpn * @return true if is the same or one of the children is the same, false * otherwise */ public boolean contains(EventProducerNode anEpn) { if (this == anEpn) return true; if (!childrenNodes.isEmpty()) { for (EventProducerNode epn : childrenNodes) { if (epn.contains(anEpn)) return true; } } return false; } /** * Provide all the children nodes that contain the epn given as input * * @param epns * List of epn * @return The list of all the epn containing all the epn provided in * parameters */ public List<EventProducerNode> containsAll(List<EventProducerNode> epns) { ArrayList<EventProducerNode> containingEpn = new ArrayList<EventProducerNode>(); boolean containsAll = true; // Check if we contains all for (EventProducerNode anEpn : epns) { if (!contains(anEpn)) { containsAll = false; break; } } // And if so, add ourself if (containsAll) containingEpn.add(this); // Recursively check for the children nodes if (!childrenNodes.isEmpty()) { for (EventProducerNode epnChild : this.getChildrenNodes()) { containingEpn.addAll(epnChild.containsAll(epns)); } } return containingEpn; } /** * Check whether or not a node and its children is included within the * given boundaries * * @param start * the starting boundary * @param end * the ending boundaries * @return The list of nodes contains within the given boundaries */ public List<EventProducerNode> withinBoundary(int start, int end) { ArrayList<EventProducerNode> containedEpn = new ArrayList<EventProducerNode>(); // Check if we are within the boundaries if ((index + weight >= start && index + weight <= end) || (index >= start && index <= end)) containedEpn.add(this); // Recursively check for the children nodes if (!childrenNodes.isEmpty()) { for (EventProducerNode epnChild : this.getChildrenNodes()) { containedEpn.addAll(epnChild.withinBoundary(start, end)); } } return containedEpn; } /** * Search for all the event producers in the hierarchy of the current * epn (including itself) * * @return the list of all the found event producers */ public ArrayList<EventProducer> getContainedProducers() { ArrayList<EventProducer> producers = new ArrayList<EventProducer>(); producers.add(this.me); // Recursively get producers of the children nodes if (!childrenNodes.isEmpty()) { for (EventProducerNode epnChild : this.getChildrenNodes()) { producers.addAll(epnChild.getContainedProducers()); } } return producers; } } private Map<Integer, EventProducerNode> eventProducerNodes = new HashMap<Integer, EventProducerNode>(); private Map<Integer, EventProducerNode> orphans = new HashMap<Integer, EventProducerNode>(); private Map<Integer, EventProducerNode> leaves = new HashMap<Integer, EventProducerNode>(); // Contains EP that are not leaves but still produces events private Map<Integer, EventProducerNode> pseudoLeaves = new HashMap<Integer, EventProducerNode>(); private Map<Integer, EventProducer> eventProducers = new HashMap<Integer, EventProducer>(); private ArrayList<EventProducerNode> aggLeaves = new ArrayList<EventProducerNode>(); private EventProducerNode root = null; protected int maxHierarchyLevel; protected int maxID = 0; protected Microscopic3DDescription microModel3D; public EventProducerHierarchy(List<EventProducer> eventProducers, Microscopic3DDescription microModel3D) throws OcelotlException { super(); for (EventProducer ep : eventProducers) { this.eventProducers.put(ep.getId(), ep); if (ep.getId() > maxID) maxID = ep.getId(); } root = null; this.microModel3D = microModel3D; maxHierarchyLevel = 0; setHierarchy(); } private void setHierarchy() throws OcelotlException { for (EventProducer ep : eventProducers.values()) { if (!eventProducerNodes.containsKey(ep.getId())) eventProducerNodes.put(ep.getId(), new EventProducerNode(ep)); } // If there are some node with no parent if (!orphans.isEmpty()) { throw new OcelotlException(OcelotlException.INCOMPLETE_HIERARCHY); } root.setWeight(); root.setChildIndex(); } public void setParts(EventProducer ep, List<Integer> parts) { eventProducerNodes.get(ep.getId()).setParts(parts); } public Map<Integer, EventProducerNode> getEventProducerNodes() { return eventProducerNodes; } public Map<Integer, EventProducerNode> getLeaves() { return leaves; } public Map<Integer, EventProducerNode> getNodes() { Map<Integer, EventProducerNode> nodes = new HashMap<Integer, EventProducerNode>(); for (int id : eventProducerNodes.keySet()) { if (!leaves.containsKey(id) && root.getID() != id) nodes.put(id, eventProducerNodes.get(id)); } return nodes; } public Map<Integer, EventProducer> getEventProducers() { return eventProducers; } public EventProducerNode getRoot() { return root; } public Map<Integer, EventProducerNode> getPseudoLeaves() { return pseudoLeaves; } public void setPseudoLeaves(Map<Integer, EventProducerNode> pseudoLeaves) { this.pseudoLeaves = pseudoLeaves; } public void setValues(HashMap<EventProducer, Object> values) { for (EventProducer ep : values.keySet()) eventProducerNodes.get(ep.getId()).setValues(values.get(ep)); } public void setValues(EventProducer ep, Object values) { eventProducerNodes.get(ep.getId()).setValues(values); } public void setParentValues(EventProducer ep, Object values) { eventProducerNodes.get(ep.getId()).setParentValues(values); } public void setParts(int id, List<Integer> parts) { eventProducerNodes.get(id).setParts(parts); } public int getParentID(int id) { return eventProducerNodes.get(id).getParentNode().getID(); } public Object getValues(int id) { return eventProducerNodes.get(id).getValues(); } public int getMaxHierarchyLevel() { return maxHierarchyLevel; } public void setMaxHierarchyLevel(int maxHierarchyLevel) { this.maxHierarchyLevel = maxHierarchyLevel; } /** * Get all the producer nodes of a given level of hierarchy * * @param hierarchyLevel * the wanted hierarchy level * @return the list of corresponding event producer nodes */ public ArrayList<EventProducerNode> getEventProducerNodesFromHierarchyLevel( int hierarchyLevel) { ArrayList<EventProducerNode> selectedEpn = new ArrayList<EventProducerNode>(); for (EventProducerNode epn : eventProducerNodes.values()) { if (epn.getHierarchyLevel() == hierarchyLevel) selectedEpn.add(epn); } return selectedEpn; } /** * Find the node the lowest in the hierarchy that contains all the event * producer nodes given as arguments * * @param epns * List of event producer nodes that we want to embedded * @return The found event producer node */ public EventProducerNode findSmallestContainingNode( List<EventProducerNode> epns) { ArrayList<EventProducerNode> containingEpn = new ArrayList<EventProducerNode>(); containingEpn.addAll(root.containsAll(epns)); EventProducerNode smallestContainingNode = containingEpn.get(0); // Find the deepest epn in the hierarchy (should be unique) for (EventProducerNode epn : containingEpn) { if (epn.getHierarchyLevel() > smallestContainingNode .getHierarchyLevel()) smallestContainingNode = epn; } return smallestContainingNode; } /** * Search for all the leave nodes that are within the given boundaries * * @param start * the starting boundary * @param end * the ending boundaries * @return The list of nodes contains within the given boundaries */ public ArrayList<EventProducerNode> findNodeWithin(int start, int end) { ArrayList<EventProducerNode> containedEpn = new ArrayList<EventProducerNode>(); for (EventProducerNode epn : leaves.values()) { if ((epn.index + epn.weight > start && epn.index + epn.weight < end) || (epn.index >= start && epn.index <= end)) containedEpn.add(epn); } if (containedEpn.isEmpty()) { for (EventProducerNode epn : leaves.values()) { if (start >= epn.index && end <= epn.index + epn.weight) containedEpn.add(epn); } } return containedEpn; } /** * Add leaf for each producer that produce events but are not leaves * * @param activeProducers * the list of all active producers */ public void buildLeavesFromActiveProducers( List<EventProducer> activeProducers, boolean spatialSelection, List<EventProducerNode> selectedNodes) { for (EventProducer anEP : activeProducers) { // If active but not a leaf if (!leaves.containsKey(anEP.getId())) { // Check if the leave is part of a spatial selection if (spatialSelection) { boolean selected = false; for (EventProducerNode epn : selectedNodes) if (epn.getMe().getId() == anEP.getId()) selected = true; if (!selected) continue; } logger.debug("Creating new leave for event prod " + anEP.getName() + ", " + anEP.getId()); EventProducerNode newNode = new EventProducerNode(anEP, eventProducerNodes.get(anEP.getId())); // Copy the values of the parent node newNode.setValues(eventProducerNodes.get(anEP.getId()).getValues()); // Add it to the leaves but with the key of the parent ID leaves.put(anEP.getId(), newNode); // Add it to the list of epn, but with their own id eventProducerNodes.put(newNode.getID(), newNode); } // Case where only one false leaf was selected else if(leaves.get(anEP.getId()).getParentNode() == null) { logger.debug("Creating new leave for event prod " + anEP.getName() + ", " + anEP.getId()); EventProducerNode newNode = new EventProducerNode(anEP, eventProducerNodes.get(anEP.getId())); // Copy the values of the parent node newNode.setValues(eventProducerNodes.get(anEP.getId()).getValues()); // Add it to the leaves but with the key of the parent ID leaves.put(anEP.getId(), newNode); // Add it to the list of epn, but with their own id eventProducerNodes.put(newNode.getID(), newNode); } } // Update weight and index root.setWeight(); root.setChildIndex(); } /** * Get leaf producers that are under a given node in the hierarchy * * @param aNode * the node from which we want to get the leaves * @return the leaves */ public ArrayList<EventProducerNode> getLeaves(EventProducerNode aNode) { ArrayList<EventProducerNode> theLeaves = new ArrayList<EventProducerNode>(); // If the node is a leaf if (leaves.values().contains(aNode)) { theLeaves.add(aNode); return theLeaves; } for (EventProducerNode aLeaf : leaves.values()) { EventProducerNode parent = aLeaf.getParentNode(); while (parent != aNode && parent != root && parent != null) { parent = parent.getParentNode(); } if (parent == aNode) theLeaves.add(aLeaf); } return theLeaves; } }