// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text.Json.Nodes;
using Microsoft.TemplateEngine.Abstractions.Mount;
using Microsoft.TemplateEngine.Core;
using Microsoft.TemplateEngine.Core.Contracts;
using Microsoft.TemplateEngine.Core.Operations;
using Microsoft.TemplateEngine.Orchestrator.RunnableProjects.Abstractions;
using Microsoft.TemplateEngine.Utils;
namespace Microsoft.TemplateEngine.Orchestrator.RunnableProjects.OperationConfig
{
internal class ConditionalConfig : IOperationConfig
{
public string Key => Conditional.OperationName;
public Guid Id => new Guid("3E8BCBF0-D631-45BA-A12D-FBF1DE03AA38");
public IEnumerable<IOperationProvider> ConfigureFromJson(string configuration, IDirectory templateRoot)
{
JsonObject rawConfiguration = JExtensions.ParseJsonObject(configuration);
string? commentStyle = rawConfiguration.ToString("style");
IEnumerable<IOperationProvider> operations = string.IsNullOrEmpty(commentStyle) || string.Equals(commentStyle, "custom", StringComparison.OrdinalIgnoreCase)
? ConditionalCustomConfig.ConfigureFromJObject(rawConfiguration)
: string.Equals(commentStyle, "line", StringComparison.OrdinalIgnoreCase)
? ConditionalLineCommentConfig.ConfigureFromJObject(rawConfiguration)
: string.Equals(commentStyle, "block", StringComparison.OrdinalIgnoreCase)
? (IEnumerable<IOperationProvider>)ConditionalBlockCommentConfig.ConfigureFromJObject(rawConfiguration)
: throw new TemplateAuthoringException($"Template authoring error. Invalid comment style [{commentStyle}].", "style");
foreach (IOperationProvider op in operations)
{
yield return op;
}
}
internal static IReadOnlyList<IOperationProvider> ConditionalSetup(ConditionalType style, EvaluatorType evaluatorType, bool wholeLine, bool trimWhiteSpace, string? id)
{
List<IOperationProvider> setup;
switch (style)
{
case ConditionalType.MSBuild:
setup = new List<IOperationProvider>();
setup.AddRange(MSBuildConditionalSetup(evaluatorType, wholeLine, trimWhiteSpace, "msbuild-conditional"));
setup.AddRange(ConditionalBlockCommentConfig.GenerateConditionalSetup("<!--", "-->"));
break;
case ConditionalType.Xml:
setup = ConditionalBlockCommentConfig.GenerateConditionalSetup("<!--", "-->");
break;
case ConditionalType.Razor:
setup = ConditionalBlockCommentConfig.GenerateConditionalSetup("@*", "*@");
break;
case ConditionalType.CLineComments:
setup = ConditionalLineCommentConfig.GenerateConditionalSetup("//");
break;
case ConditionalType.CNoComments:
setup = CStyleNoCommentsConditionalSetup(evaluatorType, wholeLine, trimWhiteSpace, id);
break;
case ConditionalType.CBlockComments:
setup = ConditionalBlockCommentConfig.GenerateConditionalSetup("/*", "*/");
break;
case ConditionalType.HashSignLineComment:
// Most line comment conditional tags use: <comment symbol><keyword prefix><keyword>
// But for this one, the '#' comment symbol is all that's needed, so it uses an empty keyword prefix.
// So we end up with regular conditionals such as '#if', '#else'
// and actions such as '##if'
ConditionalKeywords keywords = new ConditionalKeywords()
{
KeywordPrefix = string.Empty
};
setup = ConditionalLineCommentConfig.GenerateConditionalSetup("#", keywords, new ConditionalOperationOptions());
break;
case ConditionalType.RemLineComment:
setup = ConditionalLineCommentConfig.GenerateConditionalSetup("rem ");
break;
case ConditionalType.HamlLineComment:
setup = ConditionalLineCommentConfig.GenerateConditionalSetup("-#");
break;
case ConditionalType.JsxBlockComment:
setup = ConditionalBlockCommentConfig.GenerateConditionalSetup("{/*", "*/}");
break;
case ConditionalType.VB:
setup = ConditionalLineCommentConfig.GenerateConditionalSetup(
string.Empty,
new ConditionalKeywords
{
IfKeywords = new[] { "If" },
ElseIfKeywords = new[] { "ElseIf" },
ElseKeywords = new[] { "Else" },
EndIfKeywords = new[] { "End If" },
KeywordPrefix = "#"
},
new ConditionalOperationOptions
{
EvaluatorType = EvaluatorType.VB,
WholeLine = true
});
break;
default:
throw new Exception($"Unrecognized conditional type {style}");
}
return setup;
}
// Nice to have: Generalize this type of setup similarly to Line, Block, & Custom
internal static List<IOperationProvider> MSBuildConditionalSetup(EvaluatorType evaluatorType, bool wholeLine, bool trimWhiteSpace, string id)
{
ConditionEvaluator evaluator = EvaluatorSelector.Select(evaluatorType);
IOperationProvider conditional = new InlineMarkupConditional(
new MarkupTokens(
"<".TokenConfig(),
"</".TokenConfig(),
">".TokenConfig(),
"/>".TokenConfig(),
"Condition=\"".TokenConfig(),
"\"".TokenConfig(),
"<!--".TokenConfig(),
"-->".TokenConfig()),
wholeLine,
trimWhiteSpace,
evaluator,
"$({0})",
id,
true);
return new List<IOperationProvider>()
{
conditional
};
}
// Nice to have: Generalize this type of setup similarly to Line, Block, & Custom
internal static List<IOperationProvider> CStyleNoCommentsConditionalSetup(EvaluatorType evaluatorType, bool wholeLine, bool trimWhiteSpace, string? id)
{
ConditionalKeywords defaultKeywords = new ConditionalKeywords();
List<ITokenConfig> ifTokens = new List<ITokenConfig>();
List<ITokenConfig> elseifTokens = new List<ITokenConfig>();
List<ITokenConfig> elseTokens = new List<ITokenConfig>();
List<ITokenConfig> endifTokens = new List<ITokenConfig>();
foreach (string ifKeyword in defaultKeywords.IfKeywords)
{
ifTokens.Add($"{defaultKeywords.KeywordPrefix}{ifKeyword}".TokenConfig());
}
foreach (string elseifKeyword in defaultKeywords.ElseIfKeywords)
{
elseifTokens.Add($"{defaultKeywords.KeywordPrefix}{elseifKeyword}".TokenConfig());
}
foreach (string elseKeyword in defaultKeywords.ElseKeywords)
{
elseTokens.Add($"{defaultKeywords.KeywordPrefix}{elseKeyword}".TokenConfig());
}
foreach (string endifKeyword in defaultKeywords.EndIfKeywords)
{
endifTokens.Add($"{defaultKeywords.KeywordPrefix}{endifKeyword}".TokenConfig());
}
ConditionalTokens tokens = new ConditionalTokens
{
IfTokens = ifTokens,
ElseTokens = elseTokens,
ElseIfTokens = elseifTokens,
EndIfTokens = endifTokens
};
ConditionEvaluator evaluator = EvaluatorSelector.Select(evaluatorType);
IOperationProvider conditional = new Conditional(tokens, wholeLine, trimWhiteSpace, evaluator, id, true);
return new List<IOperationProvider>()
{
conditional
};
}
}
}