// 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.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace OpenTap
{
///
/// Represents a stack of ITypeDataProvider/IStackedTypeDataProvider that is used to get TypeData for a given type.
/// The providers on this stack are called in order until a provider returuns a
///
public class TypeDataProviderStack
{
object[] providers;
int offset = 0;
internal TypeDataProviderStack()
{
offset = 0;
providers = GetProviders();
}
private TypeDataProviderStack(object[] providers, int providerOffset)
{
this.providers = providers;
this.offset = providerOffset;
}
/// Gets the type data from an object.
/// The object to get type information for.
/// A representation of the type of the specified object or null if no providers can handle the specified type of object.
public ITypeData GetTypeData(object obj)
{
if (obj == null)
return null;
while (offset < providers.Length)
{
var provider = providers[offset];
offset++;
try
{
if (provider is IStackedTypeDataProvider sp)
{
var newStack = new TypeDataProviderStack(providers, offset);
if (sp.GetTypeData(obj, newStack) is ITypeData found)
return found;
}
else if (provider is ITypeDataProvider p)
{
if (p.GetTypeData(obj) is ITypeData found)
return found;
}
}
catch (Exception error)
{
logProviderError(provider, error);
}
}
if (!failedGetWarnHit)
{
// Todo: Change this method to never return null and throw in this case. Just added this warning for now, to see if this ever happens
log.Warning("Could not get TypeData for {0}", obj.GetType().FullName);
failedGetWarnHit = true;
}
return null;
}
static bool failedGetWarnHit = false;
static TraceSource log = Log.CreateSource("TypeDataProvider");
static void logProviderError(object provider, Exception error)
{
var log = Log.CreateSource(provider.GetType().Name);
log.Error("Unhandled error occured in type resolution: {0}", error.Message);
log.Debug(error);
}
/// Gets the type data from an identifier.
/// The identifier to get type information for.
/// A representation of the type specified by identifier or null if no providers can handle the specified identifier.
public ITypeData GetTypeData(string identifier)
{
if (identifier == null) return null;
while (offset < providers.Length)
{
var provider = providers[offset];
offset++;
try
{
if (provider is IStackedTypeDataProvider sp)
{
var newStack = new TypeDataProviderStack(providers, offset);
if (sp.GetTypeData(identifier, newStack) is ITypeData found)
return found;
}
else if (provider is ITypeDataProvider p)
{
if (p.GetTypeData(identifier) is ITypeData found)
return found;
}
}
catch (Exception error)
{
logProviderError(provider, error);
}
}
return null;
}
static object providersCacheLockObj = new object();
static object[] providersCache = new object[0];
static readonly HashSet badProviders = new HashSet();
static int lastCount = 0;
static object[] GetProviders()
{
var providers1 = TypeData.FromType(typeof(IStackedTypeDataProvider)).DerivedTypes;
var providers2 = TypeData.FromType(typeof(ITypeDataProvider)).DerivedTypes;
int l1 = providers1.Count();
int l2 = providers2.Count();
if (lastCount == l1 + l2) return providersCache;
lock (providersCacheLockObj)
{
if (lastCount == l1 + l2) return providersCache;
Dictionary