public static SimpleConfigModel FromJObject(JObject source)
        {
            SimpleConfigModel tmp = new SimpleConfigModel();

            tmp.Author          = source.ToString(nameof(tmp.Author));
            tmp.Classifications = source.ArrayAsStrings(nameof(tmp.Classifications));
            tmp.DefaultName     = source.ToString(nameof(DefaultName));
            tmp.GroupIdentity   = source.ToString(nameof(GroupIdentity));
            tmp.Guids           = source.ArrayAsGuids(nameof(tmp.Guids));
            tmp.Identity        = source.ToString(nameof(tmp.Identity));
            tmp.Name            = source.ToString(nameof(tmp.Name));
            tmp.ShortName       = source.ToString(nameof(tmp.ShortName));
            tmp.SourceName      = source.ToString(nameof(tmp.SourceName));

            List <ExtendedFileSource> sources = new List <ExtendedFileSource>();

            tmp.Sources = sources;

            foreach (JObject item in source.Items <JObject>(nameof(tmp.Sources)))
            {
                ExtendedFileSource src = new ExtendedFileSource();
                sources.Add(src);
                src.CopyOnly = item.Get <JToken>(nameof(src.CopyOnly));
                src.Exclude  = item.Get <JToken>(nameof(src.Exclude));
                src.Include  = item.Get <JToken>(nameof(src.Include));

                List <SourceModifier> modifiers = new List <SourceModifier>();
                src.Modifiers = modifiers;
                foreach (JObject entry in item.Items <JObject>(nameof(src.Modifiers)))
                {
                    SourceModifier modifier = new SourceModifier();
                    modifier.Condition = entry.ToString(nameof(modifier.Condition));
                    modifier.CopyOnly  = entry.Get <JToken>(nameof(modifier.CopyOnly));
                    modifier.Exclude   = entry.Get <JToken>(nameof(modifier.Exclude));
                    modifier.Include   = entry.Get <JToken>(nameof(modifier.Include));
                    modifiers.Add(modifier);
                }

                src.Source = item.ToString(nameof(src.Source));
                src.Target = item.ToString(nameof(src.Target));
            }

            Dictionary <string, ISymbolModel> symbols = new Dictionary <string, ISymbolModel>(StringComparer.Ordinal);

            tmp.Symbols = symbols;
            foreach (JProperty prop in source.PropertiesOf(nameof(tmp.Symbols)))
            {
                JObject obj = prop.Value as JObject;

                if (obj == null)
                {
                    continue;
                }

                ISymbolModel model = SymbolModelConverter.GetModelForObject(obj);

                if (model != null)
                {
                    symbols[prop.Name] = model;
                }
            }

            tmp.Tags = source.ToStringDictionary(StringComparer.OrdinalIgnoreCase, nameof(tmp.Tags));

            return(tmp);
        }
        public static SimpleConfigModel FromJObject(IEngineEnvironmentSettings environmentSettings, JObject source, JObject localeSource = null)
        {
            ILocalizationModel localizationModel = LocalizationFromJObject(localeSource);

            SimpleConfigModel config = new SimpleConfigModel()
            {
                Author              = localizationModel?.Author ?? source.ToString(nameof(config.Author)),
                Classifications     = source.ArrayAsStrings(nameof(config.Classifications)),
                DefaultName         = source.ToString(nameof(DefaultName)),
                Description         = localizationModel?.Description ?? source.ToString(nameof(Description)) ?? string.Empty,
                GroupIdentity       = source.ToString(nameof(GroupIdentity)),
                Guids               = source.ArrayAsGuids(nameof(config.Guids)),
                Identity            = source.ToString(nameof(config.Identity)),
                Name                = localizationModel?.Name ?? source.ToString(nameof(config.Name)),
                ShortName           = source.ToString(nameof(config.ShortName)),
                SourceName          = source.ToString(nameof(config.SourceName)),
                PlaceholderFilename = source.ToString(nameof(config.PlaceholderFilename)),
                EnvironmentSettings = environmentSettings
            };

            List <ExtendedFileSource> sources = new List <ExtendedFileSource>();

            config.Sources = sources;

            foreach (JObject item in source.Items <JObject>(nameof(config.Sources)))
            {
                ExtendedFileSource src = new ExtendedFileSource();
                sources.Add(src);
                src.CopyOnly  = item.Get <JToken>(nameof(src.CopyOnly));
                src.Exclude   = item.Get <JToken>(nameof(src.Exclude));
                src.Include   = item.Get <JToken>(nameof(src.Include));
                src.Condition = item.ToString(nameof(src.Condition));

                List <SourceModifier> modifiers = new List <SourceModifier>();
                src.Modifiers = modifiers;
                foreach (JObject entry in item.Items <JObject>(nameof(src.Modifiers)))
                {
                    SourceModifier modifier = new SourceModifier
                    {
                        Condition = entry.ToString(nameof(modifier.Condition)),
                        CopyOnly  = entry.Get <JToken>(nameof(modifier.CopyOnly)),
                        Exclude   = entry.Get <JToken>(nameof(modifier.Exclude)),
                        Include   = entry.Get <JToken>(nameof(modifier.Include)),
                        Rename    = entry.Get <JObject>(nameof(modifier.Rename))
                    };
                    modifiers.Add(modifier);
                }

                src.Source = item.ToString(nameof(src.Source));
                src.Target = item.ToString(nameof(src.Target));
            }

            Dictionary <string, ISymbolModel> symbols = new Dictionary <string, ISymbolModel>(StringComparer.Ordinal);

            // tags are being deprecated from template configuration, but we still read them for backwards compatibility.
            // They're turned into symbols here, which eventually become tags.
            config._tagsDeprecated = source.ToStringDictionary(StringComparer.OrdinalIgnoreCase, nameof(config.Tags));
            IReadOnlyDictionary <string, ISymbolModel> symbolsFromTags = ConvertDeprecatedTagsToParameterSymbols(config._tagsDeprecated);

            foreach (KeyValuePair <string, ISymbolModel> tagSymbol in symbolsFromTags)
            {
                if (!symbols.ContainsKey(tagSymbol.Key))
                {
                    symbols.Add(tagSymbol.Key, tagSymbol.Value);
                }
            }

            config.Symbols = symbols;
            foreach (JProperty prop in source.PropertiesOf(nameof(config.Symbols)))
            {
                JObject obj = prop.Value as JObject;

                if (obj == null)
                {
                    continue;
                }

                IParameterSymbolLocalizationModel symbolLocalization = null;
                if (localizationModel != null)
                {
                    localizationModel.ParameterSymbols.TryGetValue(prop.Name, out symbolLocalization);
                }

                ISymbolModel model = SymbolModelConverter.GetModelForObject(obj, symbolLocalization);

                if (model != null)
                {
                    symbols[prop.Name] = model;
                }
            }

            config.PostActionModel = RunnableProjects.PostActionModel.ListFromJArray(source.Get <JArray>("PostActions"), localizationModel?.PostActions);
            config.PrimaryOutputs  = CreationPathModel.ListFromJArray(source.Get <JArray>(nameof(PrimaryOutputs)));

            // Custom operations at the global level
            JToken globalCustomConfigData = source[nameof(config.CustomOperations)];

            if (globalCustomConfigData != null)
            {
                config.CustomOperations = CustomFileGlobModel.FromJObject((JObject)globalCustomConfigData, string.Empty);
            }
            else
            {
                config.CustomOperations = null;
            }

            // Custom operations for specials
            IReadOnlyDictionary <string, JToken> allSpecialOpsConfig = source.ToJTokenDictionary(StringComparer.OrdinalIgnoreCase, "SpecialCustomOperations");
            List <ICustomFileGlobModel>          specialCustomSetup  = new List <ICustomFileGlobModel>();

            foreach (KeyValuePair <string, JToken> globConfigKeyValue in allSpecialOpsConfig)
            {
                string globName = globConfigKeyValue.Key;
                JToken globData = globConfigKeyValue.Value;

                CustomFileGlobModel globModel = CustomFileGlobModel.FromJObject((JObject)globData, globName);
                specialCustomSetup.Add(globModel);
            }

            config.SpecialCustomSetup = specialCustomSetup;

            // localization operations for individual files
            Dictionary <string, IReadOnlyList <IOperationProvider> > localizations = new Dictionary <string, IReadOnlyList <IOperationProvider> >();

            if (localizationModel != null && localizationModel.FileLocalizations != null)
            {
                foreach (FileLocalizationModel fileLocalization in localizationModel.FileLocalizations)
                {
                    List <IOperationProvider> localizationsForFile = new List <IOperationProvider>();
                    foreach (KeyValuePair <string, string> localizationInfo in fileLocalization.Localizations)
                    {
                        localizationsForFile.Add(new Replacement(localizationInfo.Key, localizationInfo.Value, null));
                    }

                    localizations.Add(fileLocalization.File, localizationsForFile);
                }
            }
            config.LocalizationOperations = localizations;

            return(config);
        }
        public static SimpleConfigModel FromJObject(JObject source, JObject localeSource = null)
        {
            ILocalizationModel localizationModel = LocalizationFromJObject(localeSource);

            SimpleConfigModel config = new SimpleConfigModel();

            config.Author              = localizationModel?.Author ?? source.ToString(nameof(config.Author));
            config.Classifications     = source.ArrayAsStrings(nameof(config.Classifications));
            config.DefaultName         = source.ToString(nameof(DefaultName));
            config.Description         = localizationModel?.Description ?? source.ToString(nameof(Description));
            config.GroupIdentity       = source.ToString(nameof(GroupIdentity));
            config.Guids               = source.ArrayAsGuids(nameof(config.Guids));
            config.Identity            = source.ToString(nameof(config.Identity));
            config.Name                = localizationModel?.Name ?? source.ToString(nameof(config.Name));
            config.ShortName           = source.ToString(nameof(config.ShortName));
            config.SourceName          = source.ToString(nameof(config.SourceName));
            config.PlaceholderFilename = source.ToString(nameof(config.PlaceholderFilename));

            List <ExtendedFileSource> sources = new List <ExtendedFileSource>();

            config.Sources = sources;

            foreach (JObject item in source.Items <JObject>(nameof(config.Sources)))
            {
                ExtendedFileSource src = new ExtendedFileSource();
                sources.Add(src);
                src.CopyOnly  = item.Get <JToken>(nameof(src.CopyOnly));
                src.Exclude   = item.Get <JToken>(nameof(src.Exclude));
                src.Include   = item.Get <JToken>(nameof(src.Include));
                src.Condition = item.ToString(nameof(src.Condition));

                List <SourceModifier> modifiers = new List <SourceModifier>();
                src.Modifiers = modifiers;
                foreach (JObject entry in item.Items <JObject>(nameof(src.Modifiers)))
                {
                    SourceModifier modifier = new SourceModifier();
                    modifier.Condition = entry.ToString(nameof(modifier.Condition));
                    modifier.CopyOnly  = entry.Get <JToken>(nameof(modifier.CopyOnly));
                    modifier.Exclude   = entry.Get <JToken>(nameof(modifier.Exclude));
                    modifier.Include   = entry.Get <JToken>(nameof(modifier.Include));
                    modifiers.Add(modifier);
                }

                src.Source = item.ToString(nameof(src.Source));
                src.Target = item.ToString(nameof(src.Target));
            }

            Dictionary <string, ISymbolModel> symbols = new Dictionary <string, ISymbolModel>(StringComparer.Ordinal);

            config.Symbols = symbols;
            foreach (JProperty prop in source.PropertiesOf(nameof(config.Symbols)))
            {
                JObject obj = prop.Value as JObject;

                if (obj == null)
                {
                    continue;
                }

                string localizedDescription = null;
                if (localizationModel != null)
                {
                    localizationModel.SymbolDescriptions.TryGetValue(prop.Name, out localizedDescription);
                }

                ISymbolModel model = SymbolModelConverter.GetModelForObject(obj, localizedDescription);

                if (model != null)
                {
                    symbols[prop.Name] = model;
                }
            }

            config.Tags            = source.ToStringDictionary(StringComparer.OrdinalIgnoreCase, nameof(config.Tags));
            config.PostActionModel = RunnableProjects.PostActionModel.ListFromJArray((JArray)source["PostActions"], localizationModel?.PostActions);

            config.PrimaryOutputs = CreationPathModel.ListFromJArray((JArray)source["PrimaryOutputs"]);

            // Custom operations at the global level
            JToken globalCustomConfigData = source[nameof(config.CustomOperations)];

            if (globalCustomConfigData != null)
            {
                config.CustomOperations = CustomFileGlobModel.FromJObject((JObject)globalCustomConfigData, string.Empty);
            }
            else
            {
                config.CustomOperations = null;
            }

            // Custom operations for specials
            IReadOnlyDictionary <string, JToken> allSpecialOpsConfig = source.ToJTokenDictionary(StringComparer.OrdinalIgnoreCase, "SpecialCustomOperations");
            List <ICustomFileGlobModel>          specialCustomSetup  = new List <ICustomFileGlobModel>();

            foreach (KeyValuePair <string, JToken> globConfigKeyValue in allSpecialOpsConfig)
            {
                string globName = globConfigKeyValue.Key;
                JToken globData = globConfigKeyValue.Value;

                CustomFileGlobModel globModel = CustomFileGlobModel.FromJObject((JObject)globData, globName);
                specialCustomSetup.Add(globModel);
            }

            config.SpecialCustomSetup = specialCustomSetup;

            // localization operations for individual files
            Dictionary <string, IReadOnlyList <IOperationProvider> > localizations = new Dictionary <string, IReadOnlyList <IOperationProvider> >();

            if (localizationModel != null && localizationModel.FileLocalizations != null)
            {
                foreach (FileLocalizationModel fileLocalization in localizationModel.FileLocalizations)
                {
                    List <IOperationProvider> localizationsForFile = new List <IOperationProvider>();
                    foreach (KeyValuePair <string, string> localizationInfo in fileLocalization.Localizations)
                    {
                        localizationsForFile.Add(new Replacement(localizationInfo.Key, localizationInfo.Value, null));
                    }

                    localizations.Add(fileLocalization.File, localizationsForFile);
                }
            }
            config.LocalizationOperations = localizations;

            return(config);
        }
        private SimpleConfigModel(JObject source, ILogger?logger, ISimpleConfigModifiers?configModifiers = null, string?filename = null)
        {
            _logger = logger;

            //TODO: improve validation not to allow null values here
            Identity = source.ToString(nameof(Identity));
            Name     = source.ToString(nameof(Name));

            Author          = source.ToString(nameof(Author));
            Classifications = source.ArrayAsStrings(nameof(Classifications));
            DefaultName     = source.ToString(nameof(DefaultName));
            Description     = source.ToString(nameof(Description)) ?? string.Empty;
            GroupIdentity   = source.ToString(nameof(GroupIdentity));
            Precedence      = source.ToInt32(nameof(Precedence));
            Guids           = source.ArrayAsGuids(nameof(Guids));

            SourceName          = source.ToString(nameof(SourceName));
            PlaceholderFilename = source.ToString(nameof(PlaceholderFilename)) !;
            GeneratorVersions   = source.ToString(nameof(GeneratorVersions));
            ThirdPartyNotices   = source.ToString(nameof(ThirdPartyNotices));
            PreferNameDirectory = source.ToBool(nameof(PreferNameDirectory));

            ShortNameList = JTokenStringOrArrayToCollection(source.Get <JToken>("ShortName"), Array.Empty <string>());
            Forms         = SetupValueFormMapForTemplate(source);

            var sources = new List <ExtendedFileSource>();

            Sources = sources;
            foreach (JObject item in source.Items <JObject>(nameof(Sources)))
            {
                ExtendedFileSource src = new ExtendedFileSource();
                sources.Add(src);
                src.CopyOnly  = item.Get <JToken>(nameof(src.CopyOnly));
                src.Exclude   = item.Get <JToken>(nameof(src.Exclude));
                src.Include   = item.Get <JToken>(nameof(src.Include));
                src.Condition = item.ToString(nameof(src.Condition));
                src.Rename    = item.Get <JObject>(nameof(src.Rename))?.ToStringDictionary().ToDictionary(x => x.Key, x => x.Value);

                List <SourceModifier> modifiers = new List <SourceModifier>();
                src.Modifiers = modifiers;
                foreach (JObject entry in item.Items <JObject>(nameof(src.Modifiers)))
                {
                    SourceModifier modifier = new SourceModifier
                    {
                        Condition = entry.ToString(nameof(modifier.Condition)),
                        CopyOnly  = entry.Get <JToken>(nameof(modifier.CopyOnly)),
                        Exclude   = entry.Get <JToken>(nameof(modifier.Exclude)),
                        Include   = entry.Get <JToken>(nameof(modifier.Include)),
                        Rename    = entry.Get <JObject>(nameof(modifier.Rename))
                    };
                    modifiers.Add(modifier);
                }

                src.Source = item.ToString(nameof(src.Source));
                src.Target = item.ToString(nameof(src.Target));
            }

            IBaselineInfo?baseline = null;

            BaselineInfo = BaselineInfoFromJObject(source.PropertiesOf("baselines"));

            if (!string.IsNullOrEmpty(configModifiers?.BaselineName))
            {
                BaselineInfo.TryGetValue(configModifiers !.BaselineName, out baseline);
            }

            Dictionary <string, ISymbolModel> symbols = new Dictionary <string, ISymbolModel>(StringComparer.Ordinal);

            // create a name symbol. If one is explicitly defined in the template, it'll override this.
            NameSymbol = SetupDefaultNameSymbol(SourceName);
            symbols.Add(NameSymbolName, NameSymbol);

            // tags are being deprecated from template configuration, but we still read them for backwards compatibility.
            // They're turned into symbols here, which eventually become tags.
            _tags = source.ToStringDictionary(StringComparer.OrdinalIgnoreCase, "tags");
            IReadOnlyDictionary <string, ISymbolModel> symbolsFromTags = ConvertDeprecatedTagsToParameterSymbols(_tags);

            foreach (KeyValuePair <string, ISymbolModel> tagSymbol in symbolsFromTags)
            {
                if (!symbols.ContainsKey(tagSymbol.Key))
                {
                    symbols.Add(tagSymbol.Key, tagSymbol.Value);
                }
            }

            _symbols = symbols;
            foreach (JProperty prop in source.PropertiesOf(nameof(Symbols)))
            {
                if (prop.Value is not JObject obj)
                {
                    continue;
                }

                string?defaultOverride = null;
                if (baseline?.DefaultOverrides != null)
                {
                    baseline.DefaultOverrides.TryGetValue(prop.Name, out defaultOverride);
                }

                ISymbolModel modelForSymbol = SymbolModelConverter.GetModelForObject(obj, defaultOverride);

                if (modelForSymbol != null)
                {
                    // The symbols dictionary comparer is Ordinal, making symbol names case-sensitive.
                    if (string.Equals(prop.Name, NameSymbolName, StringComparison.Ordinal) &&
                        symbols.TryGetValue(prop.Name, out ISymbolModel existingSymbol) &&
                        existingSymbol is ParameterSymbol existingParameterSymbol &&
                        modelForSymbol is ParameterSymbol modelForParameterSymbol)
                    {
                        // "name" symbol is explicitly defined above. If it's also defined in the template.json, it gets special handling here.
                        symbols[prop.Name] = new ParameterSymbol(modelForParameterSymbol, existingParameterSymbol.Binding, existingParameterSymbol.Forms);
                    }
                    else
                    {
                        // last in wins (in the odd case where a template.json defined a symbol multiple times)
                        symbols[prop.Name] = modelForSymbol;
                    }
                }
            }