// Copyright Keysight Technologies 2012-2019
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using OpenTap.Plugins;
namespace OpenTap
{
/// Enables callback from the OpenTAP deserializer after deserialization.
public interface IDeserializedCallback
{
///
/// Called when the object has been deserialized.
///
void OnDeserialized();
}
///
/// Can be used to control the order in which members are deserialized.
///
[AttributeUsage(AttributeTargets.Property)]
public class DeserializeOrderAttribute : Attribute
{
/// The order in which the member will be deserialized. Higher order, means it will be deserialized later. Minimum value is 0, which is also the default order of not attributed members.
public double Order { get; }
///
/// Can be used to control the order in which members are deserialized.
///
/// The order in which the member will be deserialized. Higher order, means it will be deserialized later. Minimum value is 0, which is also the default order of not attributed members.
public DeserializeOrderAttribute(double order)
{
Order = order;
}
}
///
/// Serializing/deserializing OpenTAP objects. This class mostly just orchestrates a number of serializer plugins.
///
public class TapSerializer
{
///
/// Default settings for XmlWriter.
///
public static readonly XmlWriterSettings DefaultWriterSettings =
new XmlWriterSettings { Indent = true, Encoding = new UTF8Encoding(false) };
///
/// Default settings for XmlReaders.
///
public static readonly XmlReaderSettings DefaultReaderSettings =
new XmlReaderSettings { IgnoreComments = true, IgnoreWhitespace = true };
///
/// Pushes a message to the list of errors for things that happened during load.
///
/// The element that generated the error.
///
public void PushError(XElement element, string message)
{
messages.Add(new XmlError( element, message));
}
/// Pushes an error to the list of errors for things that happened during load. Includes optional Exception value.
public void PushError(XElement element, string message, Exception e)
{
messages.Add(new XmlError( element, message, e));
}
internal void HandleError(XElement element, string message, Exception e)
{
while (e is TargetInvocationException && e.InnerException != null)
e = e.InnerException;
PushError(element, $"{message} {e.Message}", e);
}
/// Pushes a message.
internal void PushMessage(XElement elem, string s)
{
messages.Add(new XmlMessage(elem, s));
}
void LogMessages()
{
foreach (var message in messages)
{
if (message is XmlError error)
{
log.Error("{0}", message);
if (error.Exception != null)
log.Debug(error.Exception);
}
else
{
log.Info("{0}", message);
}
}
}
///
/// Deserializes an object from a XDocument.
///
///
///
///
///
///
public object Deserialize(XDocument document, ITypeData type = null, bool autoFlush = true, string path = null)
{
if (document == null)
throw new ArgumentNullException(nameof(document));
var node1 = document.Elements().First();
object serialized = null;
this.ReadPath = path;
var prevSer = currentSerializer.Value;
currentSerializer.Value = this;
ClearErrors();
using (ParameterManager.WithSanityCheckDelayed())
{
try
{
try
{
Deserialize(node1, x => serialized = x, type);
}
finally
{
currentSerializer.Value = prevSer;
}
if (autoFlush)
Flush();
}
finally
{
if(ThrowOnErrors){
if (messages.Count > 0)
{
throw new Exception("Error during reading XML: " + string.Join("\n", messages));
}
}
if (IgnoreErrors == false)
{
LogMessages();
var rs = GetSerializer();
if (rs.TestPlanChanged)
{
log.Warning("Test Plan changed due to resources missing from Bench settings.");
log.Warning("Please review these changes before saving or running the Test Plan.");
}
}
}
}
return serialized;
}
///
/// Needed by defered loading. Only required to be called if autoFlush is set to false during deserialization.
///
public void Flush()
{
while (deferredLoads.Count > 0)
{
try
{
var item = deferredLoads[0];
deferredLoads.RemoveAt(0);
item.Action();
}
catch (Exception e)
{
PushError(null, $"Caught error while finishing serialization: {e.Message}");
}
}
}
///
/// Deserializes an object from a stream.
///
///
///
///
///
///
public object Deserialize(Stream stream, bool flush = true, ITypeData type = null, string path = null)
{
if (stream == null)
throw new ArgumentNullException(nameof(stream));
return Deserialize(XDocument.Load(stream, LoadOptions.SetLineInfo), type: type, autoFlush: flush, path: path);
}
///
/// Deserializes an object from an xml text string.
///
///
///
///
///
///
public object DeserializeFromString(string text, ITypeData type = null, bool flush = true, string path = null)
{
if (text == null)
throw new ArgumentNullException(nameof(text));
using (var reader = new MemoryStream(Encoding.UTF8.GetBytes(text)))
return Deserialize(reader, flush, type, path);
}
///
/// Deserializes an object from a XML file.
///
///
///
///
///
public object DeserializeFromFile(string file, ITypeData type = null, bool flush = true)
{
if (file == null)
throw new ArgumentNullException(nameof(file));
using (var fileStream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
return Deserialize(fileStream, flush, type, file);
}
ITapSerializerPlugin[] serializers = Array.Empty();
readonly Stack