/*******************************************************************************
* Copyright (c) 2006 IBM Corporation.
* 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:
* IBM Corporation - Jeff Briggs, Henry Hughes, Ryan Morse
*******************************************************************************/
package org.eclipse.linuxtools.internal.systemtap.ui.ide.structures;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.linuxtools.internal.systemtap.ui.ide.structures.nodedata.StapTreeDataFactory;
import org.eclipse.linuxtools.systemtap.structures.TreeDefinitionNode;
import org.eclipse.linuxtools.systemtap.structures.TreeNode;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.WorkbenchException;
import org.eclipse.ui.XMLMemento;
/**
* Handles access to the cached stap tapset library tree, including reading the cache back from disk
* on startup, writing the cache to disk when the cache is initially generated, checking to make sure
* that the cache is up-to-date, and providing accessor methods to the rest of the IDE that allow other
* classes to use the cached tree data.
* @author Ryan Morse
*/
public final class TreeSettings {
private static final String FILE_NAME = "TreeSettings"; //$NON-NLS-1$
private static final String FILE_DIRECTORY = ".systemtapgui"; //$NON-NLS-1$
private static final String M_DISP = "display"; //$NON-NLS-1$
private static final String M_DATA = "data"; //$NON-NLS-1$
private static final String M_DATATYPE = "datatype"; //$NON-NLS-1$
private static final String M_DEFINITON = "definition"; //$NON-NLS-1$
private static final String M_CLICKABLE = "clickable"; //$NON-NLS-1$
private static final String M_NULL = "<null>"; //$NON-NLS-1$
private static final String M_ITEM = "item"; //$NON-NLS-1$
private static final String T_FUNCTIONS = "functionTree"; //$NON-NLS-1$
private static final String T_PROBES = "probeTree"; //$NON-NLS-1$
private static final String T_DATE = "modifiedDate"; //$NON-NLS-1$
private static final String T_VERSION = "version"; //$NON-NLS-1$
private static final String VERSION_NUMBER = "3.0"; //$NON-NLS-1$
private static TreeNode cachedFunctions;
private static TreeNode cachedProbes;
private static File settingsFile = null;
private TreeSettings() {}
/**
* Deletes the Function and Probe Alias trees that have been saved to the filesystem
* as an {@link IMemento}.
* @return <code>true</code> if the delete attempt succeeded, <code>false</code> otherwise.
*/
static boolean deleteTrees() {
boolean deleted;
try {
deleted = settingsFile.delete();
} catch (SecurityException e) {
deleted = false;
}
if (deleted) {
clearCachedTrees();
}
return deleted;
}
/**
* Saves the provided Function and Probe Alias trees into an {@link IMemento} on
* the filesystem. <p>
* Note: Both trees must be saved at the same time to better ensure that they
* are both obtained from the same tapset state.
* @param functions The Function tree to store in cache.
* @param probes The Probe Alias tree to store in cache.
* @return <code>true</code> if the caching is successful.
*/
public static synchronized boolean setTrees(TreeNode functions, TreeNode probes) {
if (functions == null || probes == null
|| functions == cachedFunctions || probes == cachedProbes
|| !isTreeFileAvailable()) {
return false;
}
XMLMemento data = XMLMemento.createWriteRoot(FILE_NAME);
writeTree(data, T_FUNCTIONS, functions);
writeTree(data, T_PROBES, probes);
data.createChild(T_DATE)
.putTextData((Long.valueOf(Calendar.getInstance().getTimeInMillis())).toString());
data.createChild(T_VERSION).putTextData(VERSION_NUMBER);
try (FileWriter writer = new FileWriter(settingsFile)) {
data.save(writer);
} catch (IOException e) {
return false;
}
clearCachedTrees();
return true;
}
/**
* Writes the tree passed in to the {@link IMemento} argument.
* @param data The {@link IMemento} to store the tree to.
* @param name The name to give to the <code>parent</code> node.
* @param tree The {@link TreeNode} to store.
*/
private static void writeTree(IMemento data, String name, TreeNode tree) {
IMemento child = data.createChild(name);
child.putString(M_DISP, tree.toString());
Object treeData = tree.getData();
if (treeData != null) {
child.putString(M_DATA, treeData.toString());
child.putString(M_DATATYPE, StapTreeDataFactory.getDataObjectID(treeData));
}
if (tree instanceof TreeDefinitionNode) {
child.putString(M_DEFINITON,
getStringFromValue(((TreeDefinitionNode) tree).getDefinition()));
}
child.putBoolean(M_CLICKABLE, tree.isClickable());
for (int i = 0, n = tree.getChildCount(); i < n; i++) {
writeTree(child, M_ITEM, tree.getChildAt(i));
}
}
private static void clearCachedTrees() {
cachedFunctions = null;
cachedProbes = null;
}
/**
* Allows access to the Tapset Function tree, which contains information about all
* functions stored in the tapset library.
* @return The {@link TreeNode} root of the Function tree.
* @since 2.0
*/
public static synchronized TreeNode getFunctionTree() {
if (cachedFunctions == null) {
cachedFunctions = readData(T_FUNCTIONS);
}
return cachedFunctions;
}
/**
* Allows access to the Tapset Probe Alias tree, which contains a list of all probe aliases
* in the tapset library.
* @return The {@link TreeNode} root of the Probe Alias tree.
* @since 2.0
*/
public synchronized static TreeNode getProbeTree() {
if (cachedProbes == null) {
cachedProbes = readData(T_PROBES);
}
return cachedProbes;
}
/**
* Reads the contents of the cached memento to recreate the stored trees.
* @return True if the read is successful.
*/
private static TreeNode readData(String section) {
IMemento data = getTreeFileMemento();
if (data == null) {
return null;
}
return readTree(data.getChild(section));
}
/**
* Opposite action as writeTree. Reconstruct a tree from a previously-saved {@link IMemento}.
* @param data The {@link IMemento} to read the tree out of.
* @return The reconstructed {@link TreeNode}.
*/
private static TreeNode readTree(IMemento data) {
String disp = data.getString(M_DISP);
String def = data.getString(M_DEFINITON);
boolean c = data.getBoolean(M_CLICKABLE);
Object d = StapTreeDataFactory.createObjectFromString(data.getString(M_DATA), data.getString(M_DATATYPE));
TreeNode parent;
if (def == null) {
parent = new TreeNode(d, disp, c);
} else {
parent = new TreeDefinitionNode(d, disp, getValueFromString(def), c);
}
for (IMemento child : data.getChildren()) {
parent.add(readTree(child));
}
return parent;
}
/**
* Returns the modification date for the tree file.
* Use this to make sure that the cache is not out of date.
* @return The datestamp for the Tree file.
*/
public synchronized static long getTreeFileDate() {
IMemento data = getTreeFileMemento();
if (data != null) {
IMemento child = data.getChild(T_DATE);
try {
return Long.parseLong(child.getTextData());
} catch (NumberFormatException e) {}
}
return -1;
}
private static IMemento getTreeFileMemento() {
if (!isTreeFileAvailable()) {
return null;
}
try (FileReader reader = new FileReader(settingsFile)) {
IMemento data = XMLMemento.createReadRoot(reader, FILE_NAME);
IMemento versionChild = data.getChild(T_VERSION);
if (versionChild != null && versionChild.getTextData().equals(VERSION_NUMBER)) {
return data;
}
return null;
} catch (IOException | WorkbenchException fnfe) {
return null;
}
}
private static boolean isTreeFileAvailable() {
if (settingsFile != null) {
return true;
}
IPath path = new Path(System.getenv("HOME")). //$NON-NLS-1$
append(FILE_DIRECTORY).append(FILE_NAME).
addFileExtension("xml"); //$NON-NLS-1$
settingsFile = path.toFile();
try {
if (!settingsFile.exists()){
// Create a new settings file-and its parent
// directories- if one does not exist.
settingsFile.getParentFile().mkdirs();
settingsFile.createNewFile();
}
} catch (IOException ioe) {
return false;
}
return true;
}
private static String getStringFromValue(String val) {
return val == null ? M_NULL : val;
}
private static String getValueFromString(String string) {
return M_NULL.equals(string) ? null : string;
}
}