#region License Information
/* HeuristicLab
* Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
*
* This file is part of HeuristicLab.
*
* HeuristicLab is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HeuristicLab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with HeuristicLab. If not, see .
*/
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.IO.Compression;
using HeuristicLab.PluginInfrastructure;
namespace HeuristicLab.Core {
///
/// Static class for serializing and deserializing objects.
///
public static class PersistenceManager {
///
/// Creates an to persist an object with xml declaration.
///
/// The created .
public static XmlDocument CreateXmlDocument() {
XmlDocument document = new XmlDocument();
document.AppendChild(document.CreateXmlDeclaration("1.0", null, null));
return document;
}
///
/// Saves the specified in the specified
/// if it has not already been serialized.
///
/// The tag name of the saved instance is its type name.
/// The guid is saved as an with tag name GUID.
/// The object that should be saved.
/// The where to save the data.
/// The dictionary of all already persisted objects. (Needed to avoid cycles.)
/// The saved .
public static XmlNode Persist(IStorable instance, XmlDocument document, IDictionary persistedObjects) {
string name = instance.GetType().Name;
name = name.Replace('`', '_');
return Persist(name, instance, document, persistedObjects);
}
///
/// Saves the specified in the specified
/// if it has not already been serialized.
///
/// The (tag)name of the .
/// The object that should be saved.
/// The where to save the data.
/// The dictionary of all already persisted objects. (Needed to avoid cycles.)
/// The saved .
public static XmlNode Persist(string name, IStorable instance, XmlDocument document, IDictionary persistedObjects) {
if(persistedObjects.ContainsKey(instance.Guid)) {
XmlNode node = document.CreateNode(XmlNodeType.Element, name, null);
XmlAttribute guidAttribute = document.CreateAttribute("GUID");
guidAttribute.Value = instance.Guid.ToString();
node.Attributes.Append(guidAttribute);
return node;
} else {
persistedObjects.Add(instance.Guid, instance);
XmlNode node = instance.GetXmlNode(name, document, persistedObjects);
return node;
}
}
///
/// Loads a persisted object from the specified .
///
/// The guid is saved as an attribute with tag name GUID. The type of the
/// persisted object is saved as attribute with tag name Type.
/// Calls instance.Populate.
/// The where the object is saved.
/// A dictionary of all already restored objects.
/// (Needed to avoid cycles.)
/// The loaded object.
public static IStorable Restore(XmlNode node, IDictionary restoredObjects) {
Guid guid = new Guid(node.Attributes["GUID"].Value);
if(restoredObjects.ContainsKey(guid)) {
return restoredObjects[guid];
} else {
Type type = Type.GetType(node.Attributes["Type"].Value, true);
IStorable instance = (IStorable)Activator.CreateInstance(type);
restoredObjects.Add(guid, instance);
instance.Populate(node, restoredObjects);
return instance;
}
}
///
/// Saves the specified in the specified file through creating an
/// .
///
/// The object that should be saved.
/// The name of the file where the should be saved.
public static void Save(IStorable instance, string filename) {
using(FileStream stream = File.Create(filename)) {
Save(instance, stream);
stream.Close();
}
}
///
/// Saves the specified in the specified
/// through creating an .
///
/// The object that should be saved.
/// The (file) stream where the object should be saved.
public static void Save(IStorable instance, Stream stream) {
XmlDocument document = PersistenceManager.CreateXmlDocument();
Dictionary dictionary = new Dictionary();
XmlNode rootNode = document.CreateElement("Root");
document.AppendChild(rootNode);
XmlNode necessaryPluginsNode = document.CreateElement("NecessaryPlugins");
rootNode.AppendChild(necessaryPluginsNode);
rootNode.AppendChild(Persist(instance, document, dictionary));
// determine the list of necessary plugins for this document
DiscoveryService service = new DiscoveryService();
List plugins = new List();
foreach(IStorable storeable in dictionary.Values) {
PluginInfo pluginInfo = service.GetDeclaringPlugin(storeable.GetType());
if(!plugins.Contains(pluginInfo)) plugins.Add(pluginInfo);
}
foreach(PluginInfo uniquePlugin in plugins) {
XmlNode necessaryPluginNode = document.CreateElement("Plugin");
XmlAttribute nameAttr = document.CreateAttribute("Name");
nameAttr.Value = uniquePlugin.Name;
XmlAttribute versionAttr = document.CreateAttribute("Version");
versionAttr.Value = uniquePlugin.Version.ToString();
necessaryPluginNode.Attributes.Append(nameAttr);
necessaryPluginNode.Attributes.Append(versionAttr);
necessaryPluginsNode.AppendChild(necessaryPluginNode);
}
document.Save(stream);
}
///
/// Loads an object from a file with the specified .
///
/// The object must be saved as an .
/// Calls .
/// The filename of the file where the data is saved.
/// The loaded object.
public static IStorable Load(string filename) {
using(FileStream stream = File.OpenRead(filename)) {
IStorable storable = Load(stream);
stream.Close();
return storable;
}
}
///
/// Loads an object from the specified .
///
/// The object must be saved as an .
/// Calls .
/// The stream from where to load the data.
/// The loaded object.
public static IStorable Load(Stream stream) {
XmlDocument doc = new XmlDocument();
doc.Load(stream);
XmlNode rootNode = doc.ChildNodes[1];
if(rootNode.Name == "Root" && rootNode.ChildNodes.Count == 2) {
// load documents that have a list of necessary plugins at the top
return PersistenceManager.Restore(rootNode.ChildNodes[1], new Dictionary());
} else {
// compatibility to load documents without list of necessary plugins
return PersistenceManager.Restore(rootNode, new Dictionary());
}
}
///
/// Loads an object from a zip file.
///
/// The zip file from where to load as byte array.
/// The loaded object.
public static IStorable RestoreFromGZip(byte[] serializedStorable) {
GZipStream stream = new GZipStream(new MemoryStream(serializedStorable), CompressionMode.Decompress);
return Load(stream);
}
///
/// Saves the specified in a zip file.
///
/// Calls .
/// The object to save.
/// The zip stream as byte array.
public static byte[] SaveToGZip(IStorable storable) {
MemoryStream memStream = new MemoryStream();
GZipStream stream = new GZipStream(memStream, CompressionMode.Compress, true);
Save(storable, stream);
stream.Close();
return memStream.ToArray();
}
///
/// Builds a meaningful string for the given with the namespace information,
/// all its arguments, the assembly name...
///
/// The type for which a string should be created.
/// A string value of this type containing different additional information.
public static string BuildTypeString(Type type) {
string assembly = type.Assembly.FullName;
assembly = assembly.Substring(0, assembly.IndexOf(", "));
StringBuilder builder = new StringBuilder();
builder.Append(type.Namespace);
builder.Append(".");
builder.Append(type.Name);
Type[] args = type.GetGenericArguments();
if(args.Length > 0) {
builder.Append("[[");
builder.Append(BuildTypeString(args[0]));
builder.Append("]");
for(int i = 1; i < args.Length; i++) {
builder.Append(",[");
builder.Append(BuildTypeString(args[i]));
builder.Append("]");
}
builder.Append("]");
}
builder.Append(", ");
builder.Append(assembly);
return builder.ToString();
}
}
}