/// <summary> /// Clone <paramref name="line"/>. /// </summary> /// <param name="factory"></param> /// <param name="line"></param> /// <returns></returns> /// <exception cref="LineException">on append error</exception> public static ILine Clone(this ILineFactory factory, ILine line) { ILine result = null; StructList16 <ILine> args = new StructList16 <ILine>(); for (ILine l = line; l != null; l = l.GetPreviousPart()) { if (l is ILineArgument || l is ILineArgumentEnumerable) { args.Add(l); } } for (int i = args.Count - 1; i >= 0; i--) { ILine l = args[i]; if (l is ILineArgumentEnumerable enumr) { foreach (ILineArgument args_ in enumr) { result = factory.Create(result, args_); } } if (l is ILineArgument arg) { result = factory.Create(result, arg); } } return(result); }
/// <summary> /// Clone <see cref="ILineKey"/> parts. /// </summary> /// <param name="line"></param> /// <param name="appender">(optional) appender to use for clone. If null uses the appender of <paramref name="line"/></param> /// <param name="parameterInfos">(optional) for checking which parameters are keys</param> /// <returns>clone of key parts</returns> /// <exception cref="LineException">If cloning failed.</exception> public static ILine CloneKey(this ILine line, ILineFactory appender = default, IParameterInfos parameterInfos = null) { if (appender == null) { appender = line.GetAppender(); } ILine result = null; if (parameterInfos == null) { appender.TryGetParameterInfos(out parameterInfos); } StructList16 <ILine> args = new StructList16 <ILine>(); for (ILine l = line; l != null; l = l.GetPreviousPart()) { if (l is ILineArgument || l is ILineArgumentEnumerable) { args.Add(l); } } for (int i = args.Count - 1; i >= 0; i--) { ILine l = args[i]; if (l is ILineParameterEnumerable lineParameters) { foreach (ILineParameter lineParameter in lineParameters) { if (lineParameter.IsCanonicalKey(parameterInfos)) { result = appender.Create <ILineCanonicalKey, string, string>(result, lineParameter.ParameterName, lineParameter.ParameterValue); } else if (lineParameter.IsNonCanonicalKey(parameterInfos)) { result = appender.Create <ILineNonCanonicalKey, string, string>(result, lineParameter.ParameterName, lineParameter.ParameterValue); } } } if (l is ILineParameter parameter) { if (parameter.IsCanonicalKey(parameterInfos)) { result = appender.Create <ILineCanonicalKey, string, string>(result, parameter.ParameterName, parameter.ParameterValue); } else if (parameter.IsNonCanonicalKey(parameterInfos)) { result = appender.Create <ILineNonCanonicalKey, string, string>(result, parameter.ParameterName, parameter.ParameterValue); } } } return(result ?? appender.Create <ILinePart>(null)); }
/// <summary> /// Append <paramref name="right"/> to <paramref name="left"/>. /// </summary> /// <param name="factory"></param> /// <param name="left">part to append to</param> /// <param name="right"></param> /// <param name="result"></param> /// <returns></returns> /// <exception cref="LineException">on append error</exception> public static bool TryConcat(this ILineFactory factory, ILine left, ILine right, out ILine result) { if (factory == null) { result = left; return(false); } ILine _result = left; StructList16 <ILine> _args = new StructList16 <ILine>(); for (ILine l = right; l != null; l = l.GetPreviousPart()) { if (l is ILineArgument || l is ILineArgumentEnumerable) { _args.Add(l); } } for (int i = _args.Count - 1; i >= 0; i--) { ILine l = _args[i]; if (l is ILineArgumentEnumerable enumr) { foreach (ILineArgument args_ in enumr) { if (!factory.TryCreate(args_, out _result)) { result = null; return(false); } } } if (l is ILineArgument args) { if (!factory.TryCreate(args, out _result)) { result = null; return(false); } } } result = _result; return(false); }
/// <summary> /// Try to clone <paramref name="line"/>. /// </summary> /// <param name="factory"></param> /// <param name="line"></param> /// <param name="clone"></param> /// <returns></returns> public static bool TryClone(this ILineFactory factory, ILine line, out ILine clone) { if (factory == null) { clone = line; return(false); } ILine result = null; StructList16 <ILine> args = new StructList16 <ILine>(); for (ILine l = line; l != null; l = l.GetPreviousPart()) { if (l is ILineArgument || l is ILineArgumentEnumerable) { args.Add(l); } } for (int i = args.Count - 1; i >= 0; i--) { ILine l = args[i]; if (l is ILineArgumentEnumerable enumr) { foreach (ILineArgument args_ in enumr) { if (!factory.TryCreate(result, args_, out result)) { clone = default; return(false); } } } if (l is ILineArgument arg) { if (!factory.TryCreate(result, args, out result)) { clone = default; return(false); } } } clone = result; return(true); }
/// <summary> /// Add an enumeration of key,value pairs. Each key will constructed a new node. /// /// If <paramref name="groupingRule"/> the nodes are laid out in the order occurance of name pattern parts. /// /// For example grouping pattern "{Type}/{Culture}{anysection}{Key}" would order nodes as following: /// <code> /// "type:MyController": { /// "key:Success": "Success", /// "culture:en:key:Success": "Success", /// } /// </code> /// /// Non-capture parts such as "/" in pattern "{Section}/{Culture}", specify separator of tree node levels. /// /// </summary> /// <param name="tree"></param> /// <param name="lines"></param> /// <param name="groupingRule"></param> /// <param name="parameterInfos"></param> /// <returns></returns> public static ILineTree AddRange(this ILineTree tree, IEnumerable <ILine> lines, ILinePattern groupingRule, IParameterInfos parameterInfos) // Todo separate to sortRule + groupingRule { // Use another method //if (groupingRule == null) { node.AddRange(lines); return node; } StructList16 <ILineParameter> parameters = new StructList16 <ILineParameter>(null); foreach (var line in lines) { parameters.Clear(); line.GetParameterParts(ref parameters); parameterListSorter.Reverse(ref parameters); // Key for the current level. ILine levelKey = null; // Build levels with this collection List <ILine> key_levels = new List <ILine>(); // Visit both lists concurrently ILineParameter next_parameter = default; if (groupingRule != null) { foreach (var part in groupingRule.AllParts) { // Is not a capture part if (part.CaptureIndex < 0) { // Non-capture part has "/", go to next level. eg. "{nn}/{nn}" if (part.Text != null && part.Text.Contains("/")) { if (levelKey != null) { key_levels.Add(levelKey); } levelKey = null; } // Next part continue; } // Capture part has "/" in prefix, start next level if (levelKey != null && part.PrefixSeparator.Contains("/")) { if (levelKey != null) { key_levels.Add(levelKey); } levelKey = null; } // Look ahead to see if there is a parameter that matches this capture part int next_parameter_ix = -1; for (int ix = 0; ix < parameters.Count; ix++) { // Copy next_parameter = parameters[ix]; // Already added before if (next_parameter.ParameterName == null) { continue; } // Get name string parameter_name = next_parameter.ParameterName; // Parameter matches the name in the pattern's capture part if (parameter_name == part.ParameterName) { next_parameter_ix = ix; break; } // Matches with "anysection" //IParameterInfo info; //if (part.ParameterName == "anysection" && ParameterInfos.Default.TryGetValue(parameter_name, out info) && info.IsSection) { next_parameter_ix = ix; break; } } // No matching parameter for this capture part if (next_parameter_ix < 0) { continue; } // This part is canonical, hint, or parameter. if (!next_parameter.IsNonCanonicalKey(parameterInfos)) { // There (may be) are other canonical parts between part_ix and next_part_is. We have to add them here. for (int ix = 0; ix < next_parameter_ix; ix++) { // Copy ILineParameter parameter = parameters[ix]; // Has been added before if ((parameter.ParameterName == null) || parameter.IsNonCanonicalKey(parameterInfos)) { continue; } // Append to level's key levelKey = LineAppender.NonResolving.Create(levelKey, LineArgument.ToArgument(parameter)); // Mark handled parameters[ix] = unused; } } // Append to level's key levelKey = LineAppender.NonResolving.Create(levelKey, LineArgument.ToArgument(next_parameter)); // Mark handled parameters[next_parameter_ix] = unused; // Yield level if (part.PostfixSeparator.Contains("/")) { key_levels.Add(levelKey); levelKey = null; } } } // Append rest of the parameters for (int ix = 0; ix < parameters.Count; ix++) { // Copy ILineParameter parameter = parameters[ix]; if (parameter.ParameterName != null && parameter.ParameterName != "String") { levelKey = LineAppender.NonResolving.Create(levelKey, LineArgument.ToArgument(parameter)); } } // yield levelKey if (levelKey != null) { key_levels.Add(levelKey); levelKey = null; } // Yield line tree.AddRecursive(key_levels, line.GetString()); key_levels.Clear(); parameters.Clear(); } return(tree); }
/// <summary> /// Build key into a string. /// </summary> /// <param name="key"></param> /// <returns></returns> public string Print(ILine key) { // List for parts StructList16 <Part> parts = new StructList16 <Part>(Part.Comparer.Default); // Iterate key int occuranceIndex = 1; for (ILine part = key; part != null; part = part.GetPreviousPart()) { if (part is ILineParameter parametrized) { // Read parameter name and value string parameterName = parametrized.ParameterName, parameterValue = part.GetParameterValue(); if (string.IsNullOrEmpty(parameterName) || parameterValue == null) { continue; } // Get description _Rule desc = null; if (parameters.TryGetValue(parameterName, out desc) && !desc.IsIncluded) { continue; } // Try default descriptions if (desc == null && part is ILineCanonicalKey) { parameters.TryGetValue("canonical", out desc); } if (desc == null && part is ILineNonCanonicalKey) { parameters.TryGetValue("noncanonical", out desc); } if (desc == null) { parameters.TryGetValue("", out desc); } // No description if (desc == null) { continue; } // This parameter is disabled if (!desc.IsIncluded) { continue; } // Count occurance index occuranceIndex--; // Add to list parts.Add(new Part { ParameterName = parameterName, ParameterValue = parameterValue, Policy = desc, Order = desc.Order + occuranceIndex }); } } // Sort list sorter.Sort(ref parts); // Calculate char count int len = 0; for (int i = 0; i < parts.Count; i++) { len += parts[i].ParameterValue.Length; // Count in separator if (i > 0) { string separator = parts[i - 1].Policy.PostfixSeparator; if (separator != null) { len += separator.Length; } else { separator = parts[i].Policy.PrefixSeparator; if (separator != null) { len += separator.Length; } } } } // Put together a string char[] chars = new char[len]; int ix = 0; for (int i = 0; i < parts.Count; i++) { string s; // Add separator if (i > 0) { s = parts[i - 1].Policy.PostfixSeparator; if (s != null) { s.CopyTo(0, chars, ix, s.Length); ix += s.Length; } else { s = parts[i].Policy.PrefixSeparator; if (s != null) { s.CopyTo(0, chars, ix, s.Length); ix += s.Length; } } } // Add text s = parts[i].ParameterValue; s.CopyTo(0, chars, ix, s.Length); ix += s.Length; } return(new string(chars)); }
/// <summary> /// Concatenate <paramref name="right"/> to <paramref name="left"/>. /// </summary> /// <param name="factory"></param> /// <param name="left">part to append to</param> /// <param name="right"></param> /// <returns></returns> /// <exception cref="LineException">on append error</exception> public static ILine ConcatIfNew(this ILineFactory factory, ILine left, ILine right) { ILine result = left; KeyValuePair <string, string>[] parameters = null; StructList16 <ILine> _args = new StructList16 <ILine>(); for (ILine l = right; l != null; l = l.GetPreviousPart()) { if (l is ILineArgument || l is ILineArgumentEnumerable) { _args.Add(l); } } for (int i = _args.Count - 1; i >= 0; i--) { ILine l = _args[i]; if (l is ILineArgumentEnumerable enumr) { foreach (ILineArgument args_ in enumr) { if (args_ is ILineArgument <ILineParameter, string, string> paramArgs && ContainsParameter(paramArgs.Argument0, paramArgs.Argument1)) { continue; } if (args_ is ILineArgument <ILineNonCanonicalKey, string, string> paramArgs_ && ContainsParameter(paramArgs_.Argument0, paramArgs_.Argument1)) { continue; } if (args_ is ILineArgument <ILineCanonicalKey, string, string> paramArgs__ && ContainsParameter(paramArgs__.Argument0, paramArgs__.Argument1)) { continue; } result = factory.Create(result, args_); } } if (l is ILineArgument args) { if (args is ILineArgument <ILineParameter, string, string> paramArgs && ContainsParameter(paramArgs.Argument0, paramArgs.Argument1)) { continue; } if (args is ILineArgument <ILineNonCanonicalKey, string, string> paramArgs_ && ContainsParameter(paramArgs_.Argument0, paramArgs_.Argument1)) { continue; } if (args is ILineArgument <ILineCanonicalKey, string, string> paramArgs__ && ContainsParameter(paramArgs__.Argument0, paramArgs__.Argument1)) { continue; } result = factory.Create(result, args); } } return(result); bool ContainsParameter(string parameterName, string parameterValue) { if (parameters == null) { parameters = left.GetParameterAsKeyValues(); } for (int i = 0; i < parameters.Length; i++) { var p = parameters[i]; if (p.Key == parameterName && p.Value == parameterValue) { return(true); } } return(false); } }