private Modifier(JObject spec, OpMode opMode, IReadOnlyDictionary <string, IFunction> functionsMap) { if (spec == null) { throw new SpecException(opMode.GetName() + " expected a spec of Map type, got 'null'."); } if (functionsMap == null || functionsMap.Count == 0) { throw new SpecException(opMode.GetName() + " expected a populated functions' map type, got " + (functionsMap == null ? "null" : "empty")); } TemplatrSpecBuilder templatrSpecBuilder = new TemplatrSpecBuilder(opMode, functionsMap); _rootSpec = new ModifierCompositeSpec(ROOT_KEY, spec, opMode, templatrSpecBuilder); }
public ModifierCompositeSpec(string key, JObject spec, OpMode opMode, TemplatrSpecBuilder specBuilder) : base(key, opMode) { var literals = new Dictionary <string, IBaseSpec>(); var computed = new List <ModifierSpec>(); List <ModifierSpec> children = specBuilder.CreateSpec(spec); // remember max explicit index from spec to expand input array at runtime // need to validate spec such that it does not specify both array and literal path element int maxExplicitIndexFromSpec = -1, confirmedMapAtIndex = -1, confirmedArrayAtIndex = -1; for (int i = 0; i < children.Count; i++) { var childSpec = children[i]; var childPathElement = childSpec.GetPathElement(); // for every child, // a) mark current index as either must be map or must be array // b) mark it as literal or computed // c) if arrayPathElement, // - make sure its an explicit index type // - save the max explicit index in spec if (childPathElement is LiteralPathElement) { confirmedMapAtIndex = i; literals[childPathElement.RawKey] = childSpec; } else if (childPathElement is ArrayPathElement childArrayPathElement) { confirmedArrayAtIndex = i; if (!childArrayPathElement.IsExplicitArrayIndex()) { throw new SpecException(opMode.GetName() + " RHS only supports explicit Array path element"); } int?explicitIndex = childArrayPathElement.GetExplicitArrayIndex(); // if explicit index from spec also enforces "[...]?" don't bother using that as max index if (!childSpec.GetCheckValue()) { maxExplicitIndexFromSpec = Math.Max(maxExplicitIndexFromSpec, explicitIndex ?? 0); } literals[explicitIndex.ToString()] = childSpec; } else { // StarPathElements evaluates to string keys in a Map, EXCEPT StarAllPathElement // which can be both all keys in a map or all indexes in a list if (!(childPathElement is StarAllPathElement)) { confirmedMapAtIndex = i; } computed.Add(childSpec); } // Bail as soon as both confirmedMapAtIndex & confirmedArrayAtIndex is set if (confirmedMapAtIndex > -1 && confirmedArrayAtIndex > -1) { throw new SpecException(opMode.GetName() + " RHS cannot mix int array index and string map key, defined spec for " + key + " contains: " + children[confirmedMapAtIndex].GetPathElement().GetCanonicalForm() + " conflicting " + children[confirmedArrayAtIndex].GetPathElement().GetCanonicalForm()); } } // set the dataType from calculated indexes _specDataType = DataType.DetermineDataType(confirmedArrayAtIndex, confirmedMapAtIndex, maxExplicitIndexFromSpec); // Only the computed children need to be sorted computed.Sort(_computedKeysComparator); computed.TrimExcess(); _literalChildren = literals; _computedChildren = computed.AsReadOnly(); // extract generic execution strategy _executionStrategy = DetermineExecutionStrategy(); }