using System;
using JetBrains.Annotations;
using JetBrains.Application.Parts;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Tree;
namespace JetBrains.EnvDTE.Host.Manager
{
///
/// Maintains the ids to be used in protocol AST
///
[SolutionComponent(Instantiation.DemandAnyThreadSafe)]
public class AstManager
{
[NotNull]
private AstContainer DetachedAstContainer { get; }
[NotNull]
private AstContainer PsiContainer { get; }
[NotNull]
private AstContainer TypeContainer { get; }
public AstManager()
{
var source = new IdSource();
DetachedAstContainer = new AstContainer(source);
PsiContainer = new AstContainer(source);
TypeContainer = new AstContainer(source);
}
[NotNull]
public TResult MapElement(
int id,
[NotNull] Func psiMapper,
[NotNull] Func declaredElementMapper,
[NotNull] Func typeMapper
)
where TNode : ITreeNode
where TElement : IDeclaredElement =>
TryMapWith(id, PsiContainer, psiMapper)
?? TryMapWith(id, DetachedAstContainer, declaredElementMapper)
?? TryMapWith(id, TypeContainer, typeMapper)
?? throw new InvalidOperationException($"Attempted to map non-existing element with id {id}");
[CanBeNull]
private static TResult TryMapWith(
int id,
[NotNull] AstContainer container,
[NotNull] Func mapper
)
where TNode : TBaseNode
where TBaseNode : class
{
var node = container.TryGetElement(id);
if (node == null) return default;
if (node is TNode correct) return mapper(correct);
throw new InvalidOperationException($"Wrong node type. Expected {typeof(TNode)}, actual {node.GetType()}");
}
public int GetOrCreateId([NotNull] ITreeNode node) => PsiContainer.GetOrCreateId(node);
public int GetOrCreateId([NotNull] IDeclaredElement element) => DetachedAstContainer.GetOrCreateId(element);
public int GetOrCreateId([NotNull] IArrayType type) => TypeContainer.GetOrCreateId(type);
}
}