protected override ConfigureOptions CreateConfigureOptions([NotNull] GlobalContext globalContext, [CanBeNull] string configureOptionsString, bool forceReload) { var localVars = new ValuesFrame(); var options = new ConfigureOptions(); ProjectionSet orderedProjections = null; _projector = null; Option.Parse(globalContext, configureOptionsString, MatcherStrategyOption.Action((args, j) => { string strategy = Option.ExtractRequiredOptionValue(args, ref j, "missing strategy"); switch (strategy) { case "S": _createProjector = (p, i) => new SimpleProjector(p, name: "default projector"); break; case "PT": _createProjector = (p, i) => new SelfOptimizingPrefixTrieProjector(p, i, 10000, name: "PT projector"); break; case "FL": _createProjector = (p, i) => new SelfOptimizingFirstLetterProjector(p, i, 10000, name: "FL projector"); break; default: Log.WriteWarning($"Unrecognized matcher optimization strategy {strategy} - using default"); break; } return(j); }), ProjectionFileOption.Action((args, j) => { string fullSourceName = Path.GetFullPath(Option.ExtractRequiredOptionValue(args, ref j, "missing projections filename")); orderedProjections = GetOrReadChildConfiguration(globalContext, () => new StreamReader(fullSourceName), fullSourceName, globalContext.IgnoreCase, "????", forceReload, localVars); return(j); }), ProjectionsOption.Action((args, j) => { orderedProjections = GetOrReadChildConfiguration(globalContext, () => new StringReader(string.Join(Environment.NewLine, args.Skip(j + 1))), ProjectionsOption.ShortName, globalContext.IgnoreCase, "????", forceReload: true, localVars: localVars); // ... and all args are read in, so the next arg index is past every argument. return(int.MaxValue); })); if (orderedProjections == null || !orderedProjections.AllProjections.Any()) { Log.WriteWarning("No projections defined"); _projector = new SimpleProjector(new Projection[0], name: "empty"); _allProjectionsForMatchCountLoggingOnly = new Projection[0]; } else { _projector = _createProjector(orderedProjections.AllProjections, globalContext.IgnoreCase); _allProjectionsForMatchCountLoggingOnly = orderedProjections.AllProjections; } return(options); }
protected TConfiguration GetOrReadChildConfiguration([NotNull] GlobalContext globalContext, Func <TextReader> createReader, string containerUri, bool ignoreCase, string fileIncludeStack, bool forceReload, ValuesFrame localVars) { TConfiguration childConfiguration; Dictionary <string, string> previousConfigValues; if (_container2configValues.TryGetValue(containerUri, out previousConfigValues)) { if (!forceReload) { // Check saved names against var differences = new StringBuilder(); foreach (var kvp in previousConfigValues) { string currentValue = globalContext.GetValue(kvp.Key); if (currentValue != kvp.Value) { differences.AppendLine($"{kvp.Key}: {currentValue} vs. {kvp.Value}"); } } if (differences.Length > 0) { throw new ApplicationException($"File {containerUri} is read with different values:{Environment.NewLine}{differences}"); } } previousConfigValues = null; // no collecting of config values! } else { // Collect new config values _container2configValues.Add(containerUri, previousConfigValues = new Dictionary <string, string>()); } if (forceReload || !_configFile2Config.TryGetValue(containerUri, out childConfiguration)) { using (var tr = createReader()) { childConfiguration = CreateConfigurationFromText(globalContext, containerUri, 0, tr, ignoreCase, fileIncludeStack + "+" + containerUri, forceReload, previousConfigValues, localVars); _configFile2Config[containerUri] = childConfiguration; } } return(childConfiguration); }
protected override ConfigureOptions CreateConfigureOptions([NotNull] GlobalContext globalContext, [CanBeNull] string configureOptionsString, bool forceReload) { var localVars = new ValuesFrame(); var options = new ConfigureOptions(); Option.Parse(globalContext, configureOptionsString, ModificationsFileOption.Action((args, j) => { string fullSourceName = Path.GetFullPath(Option.ExtractRequiredOptionValue(args, ref j, "missing modifications filename")); options.OrderedActions = GetOrReadChildConfiguration(globalContext, () => new StreamReader(fullSourceName), fullSourceName, globalContext.IgnoreCase, "????", forceReload, localVars); return(j); }), ModificationsOption.Action((args, j) => { options.OrderedActions = GetOrReadChildConfiguration(globalContext, () => new StringReader(string.Join(Environment.NewLine, args.Skip(j + 1))), ModificationsOption.ShortName, globalContext.IgnoreCase, "????", forceReload: true, localVars: localVars); // ... and all args are read in, so the next arg index is past every argument. return(int.MaxValue); }) ); return(options); }
protected void ProcessTextInner([NotNull] GlobalContext globalContext, string fullConfigFileName, int startLineNo, TextReader tr, bool ignoreCase, string fileIncludeStack, bool forceReloadConfiguration, [NotNull] Action <TConfiguration, string> onIncludedConfiguration, [NotNull] Func <string, int, string> onLineWithLineNo, [CanBeNull] Dictionary <string, string> configValueCollector, ValuesFrame localVars) { int lineNo = startLineNo; for (;;) { string line = NormalizeLine(globalContext, tr.ReadLine(), configValueCollector, localVars); if (line == null) { break; } lineNo++; try { if (line == "") { // ignore; } else if (line.StartsWith("+")) { string includeFilename = line.Substring(1).Trim(); string fullIncludeFileName = Path.Combine(Path.GetDirectoryName(fullConfigFileName) ?? @"\", includeFilename); TConfiguration childConfiguration = GetOrReadChildConfiguration(globalContext, () => new StreamReader(fullIncludeFileName), fullIncludeFileName, ignoreCase, fileIncludeStack, forceReloadConfiguration, localVars); onIncludedConfiguration(childConfiguration, fullConfigFileName); } else if (line.Contains(ASSIGN)) { KeyValuePair <string, string>?kvp = ParseVariableDefinition(line); if (kvp != null) { localVars.SetDefine(kvp.Value.Key, kvp.Value.Value, $"at {fullConfigFileName}:{lineNo}"); } } else { string errorOrNull = onLineWithLineNo(line, lineNo); // line's content has been added to result as side-effect of onLineWithLineNo(...) if (errorOrNull != null) { throw new ApplicationException($"Cannot parse line '{line}' at {fullConfigFileName}:{lineNo}; reason: {errorOrNull}"); } } } catch (Exception ex) { throw new ApplicationException($"{ex.Message}{Environment.NewLine} at {fullConfigFileName}:{lineNo}"); } } }
protected string NormalizeLine([NotNull] GlobalContext globalContext, [CanBeNull] string line, [CanBeNull] Dictionary <string, string> configValueCollector, ValuesFrame localVars) { if (line != null) { int commentStart = line.IndexOf("//", StringComparison.InvariantCulture); if (commentStart >= 0) { line = line.Substring(0, commentStart); } return(globalContext.ExpandDefinesAndHexChars(localVars.ExpandDefines(line.Trim(), null), configValueCollector).Trim()); } else { return(null); } }
protected abstract TConfiguration CreateConfigurationFromText([NotNull] GlobalContext globalContext, string fullConfigFileName, int startLineNo, TextReader tr, bool ignoreCase, string fileIncludeStack, bool forceReloadConfiguration, [CanBeNull] Dictionary <string, string> configValueCollector, ValuesFrame localVars);
protected override DependencyRuleSet CreateConfigurationFromText([NotNull] GlobalContext globalContext, string fullConfigFileName, int startLineNo, TextReader tr, bool ignoreCase, string fileIncludeStack, bool forceReloadConfiguration, Dictionary <string, string> configValueCollector, ValuesFrame localVars) { ItemType usingItemType = null; ItemType usedItemType = null; string ruleSourceName = fullConfigFileName; string previousRawUsingPattern = ""; var ruleGroups = new List <DependencyRuleGroup>(); var children = new List <DependencyRuleSet>(); DependencyRuleGroup mainRuleGroup = new DependencyRuleGroup("", globalContext.IgnoreCase, null, null, "global rule group"); DependencyRuleGroup currentGroup = mainRuleGroup; ruleGroups.Add(currentGroup); ProcessTextInner(globalContext, fullConfigFileName, startLineNo, tr, ignoreCase, fileIncludeStack, forceReloadConfiguration, onIncludedConfiguration: (e, n) => children.Add(e), onLineWithLineNo: (line, lineNo) => { if (line.StartsWith("$")) { if (currentGroup != null && !currentGroup.IsGlobalGroup) { return("$ inside '{{ ... }}' not allowed"); } else { string typeLine = line.Substring(1).Trim(); int i = typeLine.IndexOf(MAY_USE, StringComparison.Ordinal); if (i < 0) { Log.WriteError($"$-line '{line}' must contain " + MAY_USE, ruleSourceName, lineNo); throw new ApplicationException($"$-line '{line}' must contain " + MAY_USE_TAIL); } usingItemType = ItemType.New(typeLine.Substring(0, i).Trim(), globalContext.IgnoreCase); usedItemType = ItemType.New(typeLine.Substring(i + MAY_USE.Length).Trim(), globalContext.IgnoreCase); return(null); } } else if (line.EndsWith("{")) { if (currentGroup == null || usingItemType == null) { return($"Itemtypes not defined - $ line is missing in {ruleSourceName}, dependency rules are ignored"); } else if (!currentGroup.IsGlobalGroup) { return("Nested '{{ ... {{' not possible"); } else { string groupPattern = line.TrimEnd('{').Trim(); currentGroup = new DependencyRuleGroup(groupPattern, globalContext.IgnoreCase, usingItemType, usedItemType, ruleSourceName + "_" + lineNo); ruleGroups.Add(currentGroup); return(null); } } else if (line == "}") { if (currentGroup != null && !currentGroup.IsGlobalGroup) { currentGroup = mainRuleGroup; return(null); } else { return("'}}' without corresponding '... {{'"); } } else { string currentRawUsingPattern; bool ok = currentGroup.AddDependencyRules(usingItemType, usedItemType, ruleSourceName, lineNo, line, ignoreCase, previousRawUsingPattern, out currentRawUsingPattern); if (!ok) { return("Could not add dependency rule"); } else { previousRawUsingPattern = currentRawUsingPattern; return(null); } } }, configValueCollector: configValueCollector, localVars: localVars); return(new DependencyRuleSet(ruleGroups, children)); }
protected override ProjectionSet CreateConfigurationFromText([NotNull] GlobalContext globalContext, string fullConfigFileName, int startLineNo, TextReader tr, bool ignoreCase, string fileIncludeStack, bool forceReloadConfiguration, Dictionary <string, string> configValueCollector, ValuesFrame localVars) { ItemType sourceItemType = null; ItemType targetItemType = null; string ruleSourceName = fullConfigFileName; var elements = new List <IProjectionSetElement>(); ProcessTextInner(globalContext, fullConfigFileName, startLineNo, tr, ignoreCase, fileIncludeStack, forceReloadConfiguration, onIncludedConfiguration: (e, n) => elements.Add(e), onLineWithLineNo: (line, lineNo) => { if (line.StartsWith("$")) { string typeLine = line.Substring(1).Trim(); int i = typeLine.IndexOf(MAP, StringComparison.Ordinal); if (i < 0) { return($"{line}: $-line must contain " + MAP); } sourceItemType = ItemType.New(typeLine.Substring(0, i).Trim(), globalContext.IgnoreCase); targetItemType = ItemType.New(typeLine.Substring(i + MAP.Length).Trim(), globalContext.IgnoreCase); return(null); } else { bool left = line.StartsWith(ABSTRACT_IT_LEFT); bool right = line.StartsWith(ABSTRACT_IT_RIGHT); bool both = line.StartsWith(ABSTRACT_IT_BOTH); if (left || both || right) { Projection p = CreateProjection(sourceItemType, targetItemType, ruleFileName: ruleSourceName, lineNo: lineNo, rule: line.Substring(1).Trim(), ignoreCase: ignoreCase, forLeftSide: left || both, forRightSide: both || right); elements.Add(p); return(null); } else { return($"{line}: line must start with $, {ABSTRACT_IT_LEFT}, {ABSTRACT_IT_BOTH}, or {ABSTRACT_IT_RIGHT}"); } } }, configValueCollector: configValueCollector, localVars: localVars); return(new ProjectionSet(elements)); }
protected override IEnumerable <ItemAction> CreateConfigurationFromText([NotNull] GlobalContext globalContext, string fullConfigFileName, int startLineNo, TextReader tr, bool ignoreCase, string fileIncludeStack, bool forceReloadConfiguration, Dictionary <string, string> configValueCollector, ValuesFrame localVars) { var actions = new List <ItemAction>(); ProcessTextInner(globalContext, fullConfigFileName, startLineNo, tr, ignoreCase, fileIncludeStack, forceReloadConfiguration, onIncludedConfiguration: (e, n) => actions.AddRange(e), onLineWithLineNo: (line, lineNo) => { actions.Add(new ItemAction(line.Trim(), ignoreCase, fullConfigFileName, startLineNo)); return(null); }, configValueCollector: configValueCollector, localVars: localVars); return(actions); }