/*******************************************************************************
* Copyright (c) 2012-2015 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:
* Generoso Pagano - initial API and implementation
******************************************************************************/
package fr.inria.soctrace.framesoc.ui.loaders;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.inria.soctrace.framesoc.core.FramesocManager;
import fr.inria.soctrace.framesoc.core.bus.FramesocBus;
import fr.inria.soctrace.framesoc.core.bus.FramesocBusTopic;
import fr.inria.soctrace.framesoc.ui.model.FolderNode;
import fr.inria.soctrace.framesoc.ui.model.ITreeNode;
import fr.inria.soctrace.framesoc.ui.model.TraceNode;
import fr.inria.soctrace.lib.model.Trace;
import fr.inria.soctrace.lib.model.utils.SoCTraceException;
import fr.inria.soctrace.lib.search.ITraceSearch;
import fr.inria.soctrace.lib.search.TraceSearch;
import fr.inria.soctrace.lib.storage.DBObject;
/**
* Load the data of the model for the Traces view.
*
* @author "Generoso Pagano <generoso.pagano@inria.fr>"
*/
public class TraceLoader {
/**
* Trace category
*/
enum TraceCategory {
/** Raw trace */
RAW_TRACE,
/** Trace that are a result of processing other traces */
PROCESSED_TRACE;
public String getName() {
return (this == RAW_TRACE) ? "Raw Traces" : "Processed Traces";
}
}
/**
* Change performed on a trace.
*/
public enum TraceChange {
/** Trace being added */
ADD,
/** Trace being removed */
REMOVE,
/** Trace metadata being updated */
UPDATE;
}
/**
* Logger
*/
private final static Logger logger = LoggerFactory.getLogger(TraceLoader.class);
/**
* List of all system traces
*/
private List<Trace> traces;
/**
* Map of category folder nodes (root nodes)
*/
private Map<TraceCategory, FolderNode> roots;
/**
* The constructor
*/
public TraceLoader() {
roots = new HashMap<>();
for (TraceCategory cat : TraceCategory.values()) {
roots.put(cat, new FolderNode(cat.getName()));
}
}
/**
* Get an array containing the root folder nodes.
*
* @return an array containing the root folder nodes.
*/
public FolderNode[] getRoots() {
return roots.values().toArray(new FolderNode[roots.values().size()]);
}
/**
* Load traces from System DB. Note that this method loads new trace objects. Any existing root
* content is lost.
*
* @return an array containing the root folder nodes.
*/
public FolderNode[] loadFromDB() {
ITraceSearch traceSearch = null;
try {
if (!FramesocManager.getInstance().isSystemDBExisting())
return getRoots();
/* Get traces from DB */
traceSearch = new TraceSearch().initialize();
traces = traceSearch.getTraces();
traceSearch.uninitialize();
/* Prepare the tree */
buildTree(buildTraceMap(traces));
} catch (SoCTraceException e) {
logger.error(e.getMessage());
removeAll();
} finally {
TraceSearch.finalUninitialize(traceSearch);
}
return getRoots();
}
/**
* Synchronize current model with DB. Note that an old model input must be already present. This
* method keeps the existing trace objects (possibly updating them) and tree nodes, thus fixing
* any selection service issues with other views.
*
* @return the model input (an array containing the root folder nodes)
* @throws SoCTraceException
*/
public FolderNode[] synchWithDB() throws SoCTraceException {
ITraceSearch traceSearch = null;
try {
if (!FramesocManager.getInstance().isSystemDBExisting())
return getRoots();
/* Get traces from DB */
traceSearch = new TraceSearch().initialize();
traces = traceSearch.getTraces();
traceSearch.uninitialize();
// new trace map
Map<Integer, Trace> newTraces = new HashMap<>();
for (Trace t : traces) {
if(DBObject.isDBExisting(t.getDbName()))
newTraces.put(t.getId(), t);
else {
FramesocManager.getInstance().deleteTrace(t);
}
}
// linearize the old tree
List<Trace> traces = linearizeTree();
Map<TraceChange, List<Trace>> traceChangeMap = new HashMap<TraceChange, List<Trace>>();
traceChangeMap.put(TraceChange.ADD, new LinkedList<Trace>());
traceChangeMap.put(TraceChange.REMOVE, new LinkedList<Trace>());
traceChangeMap.put(TraceChange.UPDATE, new LinkedList<Trace>());
// synch old tree trace objects with new ones
Iterator<Trace> oldTracesIterator = traces.iterator();
while (oldTracesIterator.hasNext()) {
Trace t = oldTracesIterator.next();
if (newTraces.containsKey(t.getId())) {
// update or not changed
Trace newTrace = newTraces.get(t.getId());
if (!newTrace.equals(t)) {
t.synchWith(newTrace);
traceChangeMap.get(TraceChange.UPDATE).add(t);
}
// remove from new traces
newTraces.remove(t.getId());
} else {
// deleted: remove from input
traceChangeMap.get(TraceChange.REMOVE).add(t);
oldTracesIterator.remove();
}
}
// cat new traces
for (Trace nt : newTraces.values()) {
traces.add(nt);
traceChangeMap.get(TraceChange.ADD).add(nt);
}
// update the tree
updateTree(buildTraceMap(traces));
// notify the bus of changes
FramesocBus.getInstance().send(FramesocBusTopic.TOPIC_UI_TRACES_SYNCHRONIZED,
traceChangeMap);
} catch (SoCTraceException e) {
logger.error(e.getMessage());
removeAll();
throw e;
} finally {
TraceSearch.finalUninitialize(traceSearch);
}
return getRoots();
}
/**
* Synchronize the trace nodes with the trace objects contained, in order to get nodes with
* updated labels. The System DB is not used.
*
* @return the model input (an array containing the root folder nodes)
*/
public FolderNode[] synchWithModel() {
// linearize the old tree
List<Trace> traces = linearizeTree();
// update the tree
updateTree(buildTraceMap(traces));
return getRoots();
}
/**
* Get the traces. You have to call the methods that actually load traces from DB before calling
* this method.
*
* @return the loaded traces
*/
public List<Trace> getTraces() {
return traces;
}
/**
* Get a trace node from a given trace.
*
* @param t
* trace
* @return the corresponding trace node, or null if not found
*/
public TraceNode getTraceNode(Trace t) {
for (ITreeNode node : roots.values()) {
TraceNode n = getTraceNode(node, t);
if (n != null)
return n;
}
return null;
}
/**
* Remove all the elements from the root folder nodes.
*/
private void removeAll() {
for (FolderNode n : roots.values()) {
n.removeAll();
}
}
/**
* Builds the following map:
*
* <pre>
* Category <-> { TraceTypeName <-> {TraceID <-> Trace} }
* </pre>
*
* @param traces
* list of traces
* @return the trace map
*/
private Map<TraceCategory, Map<String, Map<Integer, Trace>>> buildTraceMap(List<Trace> traces) {
Map<TraceCategory, Map<String, Map<Integer, Trace>>> traceMap = new HashMap<TraceCategory, Map<String, Map<Integer, Trace>>>();
traceMap.put(TraceCategory.RAW_TRACE, new HashMap<String, Map<Integer, Trace>>());
traceMap.put(TraceCategory.PROCESSED_TRACE, new HashMap<String, Map<Integer, Trace>>());
Map<String, Map<Integer, Trace>> currentMap;
for (Trace trace : traces) {
if (trace.isProcessed())
currentMap = traceMap.get(TraceCategory.PROCESSED_TRACE);
else
currentMap = traceMap.get(TraceCategory.RAW_TRACE);
if (!currentMap.containsKey(trace.getType().getName())) {
currentMap.put(trace.getType().getName(), new HashMap<Integer, Trace>());
}
currentMap.get(trace.getType().getName()).put(trace.getId(), trace);
}
return traceMap;
}
private void buildTree(Map<TraceCategory, Map<String, Map<Integer, Trace>>> map) {
// clean the tree
removeAll();
// category
Iterator<Entry<TraceCategory, Map<String, Map<Integer, Trace>>>> iterator = map.entrySet()
.iterator();
while (iterator.hasNext()) {
Entry<TraceCategory, Map<String, Map<Integer, Trace>>> pair = iterator.next();
FolderNode currentCategory = roots.get(pair.getKey());
// trace type
Iterator<Entry<String, Map<Integer, Trace>>> it = pair.getValue().entrySet().iterator();
while (it.hasNext()) {
Entry<String, Map<Integer, Trace>> typePair = it.next();
FolderNode typeNode = new FolderNode(typePair.getKey());
// traces
for (Trace trace : typePair.getValue().values()) {
typeNode.addChild(new TraceNode(trace.getAlias(), trace));
}
currentCategory.addChild(typeNode);
}
}
}
private void updateTree(Map<TraceCategory, Map<String, Map<Integer, Trace>>> map) {
for (TraceCategory cat : TraceCategory.values()) {
updateCategory(roots.get(cat), map.get(cat));
}
}
private void updateCategory(FolderNode categoryNode, Map<String, Map<Integer, Trace>> map) {
// empty category
if (!categoryNode.hasChildren()) {
Iterator<Entry<String, Map<Integer, Trace>>> it = map.entrySet().iterator();
while (it.hasNext()) {
Entry<String, Map<Integer, Trace>> typePair = it.next();
FolderNode typeNode = new FolderNode(typePair.getKey());
for (Trace trace : typePair.getValue().values()) {
typeNode.addChild(new TraceNode(trace.getAlias(), trace));
}
categoryNode.addChild(typeNode);
}
return;
}
// non empty category
Iterator<ITreeNode> typeIterator = categoryNode.getChildren().iterator();
while (typeIterator.hasNext()) {
FolderNode typeNode = (FolderNode) typeIterator.next();
if (!map.containsKey(typeNode.getName()))
typeIterator.remove();
else {
updateType(typeNode, map.get(typeNode.getName()));
// remove type from map
map.remove(typeNode.getName());
}
}
// new types for this category
Iterator<Entry<String, Map<Integer, Trace>>> newTypeIterator = map.entrySet().iterator();
while (newTypeIterator.hasNext()) {
Entry<String, Map<Integer, Trace>> typePair = newTypeIterator.next();
FolderNode typeNode = new FolderNode(typePair.getKey());
for (Trace trace : typePair.getValue().values()) {
typeNode.addChild(new TraceNode(trace.getAlias(), trace));
}
categoryNode.addChild(typeNode);
}
}
private void updateType(FolderNode typeNode, Map<Integer, Trace> map) {
// empty type is not possible, so manage non empty case
Iterator<ITreeNode> traceIterator = typeNode.getChildren().iterator();
while (traceIterator.hasNext()) {
TraceNode traceNode = (TraceNode) traceIterator.next();
Trace t = (traceNode).getTrace();
if (!map.containsKey(t.getId())) {
// deleted
traceIterator.remove();
} else {
traceNode.setName(t.getAlias());
map.remove(t.getId());
}
}
// new traces for this type
for (Trace nt : map.values()) {
typeNode.addChild(new TraceNode(nt.getAlias(), nt));
}
}
private List<Trace> linearizeTree() {
List<Trace> traces = new LinkedList<Trace>();
if (roots.isEmpty())
return traces;
for (ITreeNode categoryNode : roots.values()) {
if (!categoryNode.hasChildren())
continue;
for (ITreeNode typeNode : categoryNode.getChildren()) {
if (!typeNode.hasChildren())
continue;
for (ITreeNode traceNode : typeNode.getChildren())
traces.add(((TraceNode) traceNode).getTrace());
}
}
return traces;
}
/**
* Recursively looks for a trace node for the given trace.
*
* @param n
* node
* @param t
* trace to look for
* @return the trace node, or null if not found
*/
private TraceNode getTraceNode(ITreeNode n, Trace t) {
if (!n.hasChildren()) {
// leaf
if (n instanceof TraceNode) {
// trace node
TraceNode tnode = (TraceNode) n;
if (tnode.getTrace().equals(t))
return tnode;
}
return null;
}
for (ITreeNode son : n.getChildren()) {
TraceNode ret = getTraceNode(son, t);
if (ret != null)
return ret;
}
return null;
}
}