public string Evaluate(WalkedPath walkedPath) { var data = ObjectEvaluate(walkedPath); if (data != null) { // Coerce a number into a string if (data.Type == JTokenType.Integer) { // the idea here being we are looking for an array index value return(data.ToString()); } // Coerce a boolean into a string if (data.Type == JTokenType.Boolean) { return(data.Value <bool>() ? "true" : "false"); } if (data.Type == JTokenType.String) { return(data.ToString()); } // If this output path has a TransposePathElement, and when we evaluate it // it does not resolve to a string, then return null } return(null); }
private void Process(JToken input, WalkedPath walkedPath) { if (input?.Type == JTokenType.Object) { // Iterate over the whole entrySet rather than the keyset with follow on gets of the values // (because the collection is modified by the recursive call) var obj = (JObject)input; foreach (var kv in obj.ToList <KeyValuePair <string, JToken> >()) { ApplyKeyToLiteralAndComputed(this, kv.Key, kv.Value, walkedPath, input); } } else if (input?.Type == JTokenType.Array) { var list = (JArray)input; for (int index = 0; index < list.Count; index++) { var subInput = list[index]; string subKeyStr = index.ToString(); ApplyKeyToLiteralAndComputed(this, subKeyStr, subInput, walkedPath, input); } } else if (input != null) { // if not a map or list, must be a scalar string scalarInput = input.ToString(); ApplyKeyToLiteralAndComputed(this, scalarInput, null, walkedPath, scalarInput); } }
/** * Use the supplied WalkedPath, in the evaluation of each of our PathElements to * build a concrete output path. Then use that output path to write the given * data to the output. * * @param data data to write * @param output data structure we are going to write the data to * @param walkedPath reference used to lookup reference values like "&1(2)" */ public void Write(JToken data, JObject output, WalkedPath walkedPath) { var evaledPaths = Evaluate(walkedPath); if (evaledPaths != null) { _traversr.Set(output, evaledPaths, data); } }
/** * This should only be used by composite specs with an '@' child * * @return null if no work was done, otherwise returns the re-parented data */ public JToken ApplyToParentContainer(string inputKey, JToken input, WalkedPath walkedPath, JToken parentContainer) { MatchedElement thisLevel = GetMatch(inputKey, walkedPath); if (thisLevel == null) { return(null); } return(PerformCardinalityAdjustment(inputKey, input, walkedPath, (JObject)parentContainer, thisLevel)); }
public MatchedElement Match(string dataKey, WalkedPath walkedPath) { string evaled = Evaluate(walkedPath); if (evaled == dataKey) { return(new MatchedElement(evaled)); } return(null); }
/** * If this CardinalitySpec matches the inputkey, then do the work of modifying the data and return true. * * @return true if this this spec "handles" the inputkey such that no sibling specs need to see it */ public override bool ApplyCardinality(string inputKey, JToken input, WalkedPath walkedPath, JToken parentContainer) { MatchedElement thisLevel = GetMatch(inputKey, walkedPath); if (thisLevel == null) { return(false); } PerformCardinalityAdjustment(inputKey, input, walkedPath, (JObject)parentContainer, thisLevel); return(true); }
public JToken Read(JToken data, WalkedPath walkedPath) { var evaledPaths = Evaluate(walkedPath); if (evaledPaths == null) { return(null); } return(_traversr.Get(data, evaledPaths)); }
public MatchedElement Match(string dataKey, WalkedPath walkedPath) { int?origSizeOptional = walkedPath.LastElement().OrigSize; if (origSizeOptional.HasValue) { return(new ArrayMatchedElement(dataKey, origSizeOptional.Value)); } else { return(new MatchedElement(dataKey)); } }
public JToken Transform(JToken input, JObject context) { var contextWrapper = new JObject(); contextWrapper.Add(ROOT_KEY, context); MatchedElement rootLpe = new MatchedElement(ROOT_KEY); WalkedPath walkedPath = new WalkedPath(); walkedPath.Add(input, rootLpe); _rootSpec.Apply(ROOT_KEY, input, walkedPath, null, contextWrapper); return(input); }
public MatchedElement Match(string dataKey, WalkedPath walkedPath) { if (StringMatch(dataKey)) { var subKeys = new List <string>(); string starPart = dataKey.Substring(_prefix.Length, dataKey.Length - _suffix.Length - _prefix.Length); subKeys.Add(starPart); return(new MatchedElement(dataKey, subKeys)); } return(null); }
/** * * @return null if no work was done, otherwise returns the re-parented data */ private JToken PerformCardinalityAdjustment(string inputKey, JToken input, WalkedPath walkedPath, JObject parentContainer, MatchedElement thisLevel) { // Add our the LiteralPathElement for this level, so that write path References can use it as &(0,0) walkedPath.Add(input, thisLevel); JToken returnValue = null; if (_cardinalityRelationship == CardinalityRelationship.MANY) { if (input is JArray) { returnValue = input; } else if (input.Type == JTokenType.Object || input.Type == JTokenType.String || input.Type == JTokenType.Integer || input.Type == JTokenType.Float || input.Type == JTokenType.Boolean) { var one = parentContainer[inputKey]; parentContainer.Remove(inputKey); var tempList = new JArray(); tempList.Add(one); returnValue = tempList; } else if (input.Type == JTokenType.Null) { returnValue = new JArray(); } parentContainer[inputKey] = returnValue; } else if (_cardinalityRelationship == CardinalityRelationship.ONE) { if (input is JArray l) { // The value is first removed from the array in order to prevent it // from being cloned (JContainers clone their inputs if they are already // part of an object graph) if (l.Count > 0) { returnValue = l[0]; l.RemoveAt(0); } parentContainer[inputKey] = returnValue; } } walkedPath.RemoveLast(); return(returnValue); }
/** * Creates an empty map/list, as required by spec, in the parent map/list at given key/index * * @param keyOrIndex of the parent object to create * @param walkedPath containing the parent object * @param opMode to determine if this write operation is allowed * @return newly created object */ public JToken Create(string keyOrIndex, WalkedPath walkedPath, OpMode opMode) { object parent = walkedPath.LastElement().TreeRef; int? origSizeOptional = walkedPath.LastElement().OrigSize; if (!Int32.TryParse(keyOrIndex, out int index)) { index = -1; } JToken value = null; if (parent is JObject map && opMode.IsApplicable(map, keyOrIndex)) { map[keyOrIndex] = value = CreateValue(); }
/** * Applies the Shiftr transform. * * @param input the JSON object to transform * @return the output object with data shifted to it * @throws com.bazaarvoice.jolt.exception.TransformException for a malformed spec or if there are issues during * the transform */ public JToken Transform(JToken input) { var output = new JObject(); // Create a root LiteralPathElement so that # is useful at the root level MatchedElement rootLpe = new MatchedElement(ROOT_KEY); WalkedPath walkedPath = new WalkedPath(); walkedPath.Add(input, rootLpe); _rootSpec.Apply(ROOT_KEY, input, walkedPath, output, null); output.TryGetValue(ROOT_KEY, out var result); return(result ?? JValue.CreateNull()); }
public void Process(IOrderedCompositeSpec spec, JToken input, WalkedPath walkedPath, JObject output, JObject context) { if (input is JObject map) { ProcessMap(spec, map, walkedPath, output, context); } else if (input is JArray list) { ProcessList(spec, list, walkedPath, output, context); } else if (input != null && input.Type != JTokenType.Null) { // if not a map or list, must be a scalar ProcessScalar(spec, ToString(input), walkedPath, output, context); } }
public JToken Evaluate(JToken inputOptional, WalkedPath walkedPath, JObject context) { JToken valueOptional = null; try { // "key": "@0", "key": literal if (_function == null) { valueOptional = _functionArgs[0].EvaluateArg(walkedPath, context); } // "key": "=abs(@(1,&0))" // this is most usual case, a single argument is passed and we need to evaluate and // pass the value, if present, to the spec function else if (_functionArgs.Length == 1) { var evaluatedArgValue = _functionArgs[0].EvaluateArg(walkedPath, context); valueOptional = evaluatedArgValue != null?_function.Apply(evaluatedArgValue) : _function.Apply(); } // "key": "=abs(@(1,&0),-1,-3)" // this is more complicated case! if args is an array, after evaluation we cannot pass a missing value wrapped in // object[] into function. In such case null will be passed however, in json null is also a valid value, so it is // upto the implementer to interpret the value. Ideally we can almost always pass a list straight from input. else if (_functionArgs.Length > 1) { var evaluatedArgs = EvaluateArgsValue(_functionArgs, context, walkedPath); valueOptional = _function.Apply(evaluatedArgs); } // // FYI this is where the "magic" happens that allows functions that take a single method // default to the current "match" rather than an explicit "reference". // Note, this does not work for functions that take more than a single input. // // "key": "=abs" else { // pass current value as arg if present valueOptional = inputOptional != null?_function.Apply(inputOptional) : _function.Apply(); } } catch (Exception) { } return(valueOptional); }
public MatchedElement Match(string dataKey, WalkedPath walkedPath) { string evaled = Evaluate(walkedPath); if (evaled == dataKey) { int?origSizeOptional = walkedPath.LastElement().OrigSize; if (origSizeOptional.HasValue) { return(new ArrayMatchedElement(evaled, origSizeOptional.Value)); } else { return(null); } } return(null); }
/** * Use the supplied WalkedPath, in the evaluation of each of our PathElements. * * If our PathElements contained a TransposePathElement, we may return null. * * @param walkedPath used to lookup/evaluate PathElement references values like "&1(2)" * @return null or fully evaluated Strings, possibly with concrete array references like "photos.[3]" */ // Visible for testing public List <string> Evaluate(WalkedPath walkedPath) { var strings = new List <string>(_elements.Count); foreach (IEvaluatablePathElement pathElement in _elements) { string evaledLeafOutput = pathElement.Evaluate(walkedPath); if (evaledLeafOutput == null) { // If this output path contains a TransposePathElement, and when evaluated, // return null, then bail return(null); } strings.Add(evaledLeafOutput); } return(strings); }
public MatchedElement Match(string dataKey, WalkedPath walkedPath) { var result = _pattern.Match(dataKey); if (!result.Success) { return(null); } var subKeys = new List <string>(); for (int index = 1; index < result.Groups.Count; index++) { subKeys.Add(result.Groups[index].Value); } return(new MatchedElement(dataKey, subKeys)); }
public MatchedElement Match(string dataKey, WalkedPath walkedPath) { if (StringMatch(dataKey)) { var subKeys = new List <string>(2); int midStart = FinMidIndex(dataKey); int midEnd = midStart + _mid.Length; string firstStarPart = dataKey.Substring(_prefix.Length, midStart - _prefix.Length); subKeys.Add(firstStarPart); string secondStarPart = dataKey.Substring(midEnd, dataKey.Length - _suffix.Length - midEnd); subKeys.Add(secondStarPart); return(new MatchedElement(dataKey, subKeys)); } return(null); }
/** * If this Spec matches the inputKey, then perform one step in the Shiftr parallel treewalk. * * Step one level down the input "tree" by carefully handling the List/Map nature the input to * get the "one level down" data. * * Step one level down the Spec tree by carefully and efficiently applying our children to the * "one level down" data. * * @return true if this this spec "handles" the inputKey such that no sibling specs need to see it */ public override bool Apply(string inputKey, JToken inputOptional, WalkedPath walkedPath, JObject output, JObject context) { MatchedElement thisLevel = _pathElement.Match(inputKey, walkedPath); if (thisLevel == null) { return(false); } // If we are a TransposePathElement, try to swap the "input" with what we lookup from the Transpose if (_pathElement is TransposePathElement tpe) { // Note the data found may not be a string, thus we have to call the special objectEvaluate // Optional, because the input data could have been a valid null. var optional = tpe.ObjectEvaluate(walkedPath); if (optional == null) { return(false); } inputOptional = optional; } // add ourselves to the path, so that our children can reference us walkedPath.Add(inputOptional, thisLevel); // Handle any special / key based children first, but don't have them block anything foreach (ShiftrSpec subSpec in _specialChildren) { subSpec.Apply(inputKey, inputOptional, walkedPath, output, context); } // Handle the rest of the children _executionStrategy.Process(this, inputOptional, walkedPath, output, context); // We are done, so remove ourselves from the walkedPath walkedPath.RemoveLast(); // we matched so increment the matchCount of our parent walkedPath.LastElement().MatchedElement.IncrementHashCount(); return(true); }
/** * This method is used when the TransposePathElement is used on the LFH as data. * * Aka, normal "evaluate" returns either a Number or a string. * * @param walkedPath WalkedPath to evaluate against * @return The data specified by this TransposePathElement. */ public JToken ObjectEvaluate(WalkedPath walkedPath) { // Grap the data we need from however far up the tree we are supposed to go PathStep pathStep = walkedPath.ElementFromEnd(_upLevel); if (pathStep == null) { return(null); } var treeRef = pathStep.TreeRef; // Now walk down from that level using the subPathReader if (_subPathReader == null) { return(treeRef); } else { return(_subPathReader.Read(treeRef, walkedPath)); } }
public string Evaluate(WalkedPath walkedPath) { switch (_arrayPathType) { case ArrayPathType.AUTO_EXPAND: return(_canonicalForm); case ArrayPathType.EXPLICIT_INDEX: return(_arrayIndex); case ArrayPathType.HASH: MatchedElement element = walkedPath.ElementFromEnd(_ref.GetPathIndex()).MatchedElement; return(element.GetHashCount().ToString()); case ArrayPathType.TRANSPOSE: string key = _transposePathElement.Evaluate(walkedPath); return(VerifyStringIsNonNegativeInteger(key)); case ArrayPathType.REFERENCE: { MatchedElement lpe = walkedPath.ElementFromEnd(_ref.GetPathIndex()).MatchedElement; string keyPart; if (_ref is IPathAndGroupReference pagr) { keyPart = lpe.GetSubKeyRef(pagr.GetKeyGroup()); } else { keyPart = lpe.GetSubKeyRef(0); } return(VerifyStringIsNonNegativeInteger(keyPart)); } default: throw new InvalidOperationException("ArrayPathType enum added two without updating this switch statement."); } }
public string Evaluate(WalkedPath walkedPath) { // Walk thru our tokens and build up a string // Use the supplied Path to fill in our token References StringBuilder output = new StringBuilder(); foreach (object token in _tokens) { if (token is string stoken) { output.Append(stoken); } else { AmpReference ref_ = (AmpReference)token; MatchedElement matchedElement = walkedPath.ElementFromEnd(ref_.GetPathIndex()).MatchedElement; string value = matchedElement.GetSubKeyRef(ref_.GetKeyGroup()); output.Append(value); } } return(output.ToString()); }
public bool Apply(string inputKey, JToken inputOptional, WalkedPath walkedPath, JObject output, JObject context) { if (output != null) { throw new TransformException("Expected a null output"); } MatchedElement thisLevel = _pathElement.Match(inputKey, walkedPath); if (thisLevel == null) { return(false); } if (!_checkValue) // there was no trailing "?" so no check is necessary { ApplyElement(inputKey, inputOptional, thisLevel, walkedPath, context); } else if (inputOptional != null) { ApplyElement(inputKey, inputOptional, thisLevel, walkedPath, context); } return(true); }
/** * If this Spec matches the inputkey, then perform one step in the parallel treewalk. * <p/> * Step one level down the input "tree" by carefully handling the List/Map nature the input to * get the "one level down" data. * <p/> * Step one level down the Spec tree by carefully and efficiently applying our children to the * "one level down" data. * * @return true if this this spec "handles" the inputkey such that no sibling specs need to see it */ public override bool ApplyCardinality(string inputKey, JToken input, WalkedPath walkedPath, JToken parentContainer) { MatchedElement thisLevel = GetPathElement().Match(inputKey, walkedPath); if (thisLevel == null) { return(false); } walkedPath.Add(input, thisLevel); // The specialChild can change the data object that I point to. // Aka, my key had a value that was a List, and that gets changed so that my key points to a ONE value if (_specialChild != null) { input = _specialChild.ApplyToParentContainer(inputKey, input, walkedPath, parentContainer); } // Handle the rest of the children Process(input, walkedPath); walkedPath.RemoveLast(); return(true); }
/** * This is the method we are trying to avoid calling. It implements the matching behavior * when we have both literal and computed children. * * For each input key, we see if it matches a literal, and it not, try to match the key with every computed child. * * Worse case : n + n * c, where * n is number of input keys * c is number of computed children */ protected static void ApplyKeyToLiteralAndComputed <T>(T spec, string subKeyStr, JToken subInputOptional, WalkedPath walkedPath, JObject output, JObject context) where T : IOrderedCompositeSpec { // if the subKeyStr found a literalChild, then we do not have to try to match any of the computed ones if (spec.GetLiteralChildren().TryGetValue(subKeyStr, out var literalChild)) { literalChild.Apply(subKeyStr, subInputOptional, walkedPath, output, context); } else { // If no literal spec key matched, iterate through all the getComputedChildren() ApplyKeyToComputed(spec.GetComputedChildren(), walkedPath, output, subKeyStr, subInputOptional, context); } }
/** * This is the main recursive method of the CardinalityTransform parallel "spec" and "input" tree walk. * * It should return true if this Spec object was able to successfully apply itself given the * inputKey and input object. * * In the context of the CardinalityTransform parallel treewalk, if this method returns a non-null object, * the assumption is that no other sibling Cardinality specs need to look at this particular input key. * * @return true if this this spec "handles" the inputkey such that no sibling specs need to see it */ public abstract bool ApplyCardinality(string inputKey, JToken input, WalkedPath walkedPath, JToken parentContainer);
public bool Apply(string inputKey, JToken inputOptional, WalkedPath walkedPath, JObject output, JObject context) { return(ApplyCardinality(inputKey, inputOptional, walkedPath, output)); }
private static JToken[] EvaluateArgsValue(FunctionArg[] functionArgs, JObject context, WalkedPath walkedPath) { JToken[] evaluatedArgs = new JToken[functionArgs.Length]; for (int i = 0; i < functionArgs.Length; i++) { FunctionArg arg = functionArgs[i]; evaluatedArgs[i] = arg.EvaluateArg(walkedPath, context); } return(evaluatedArgs); }
protected override void ApplyElement(string inputKey, JToken inputOptional, MatchedElement thisLevel, WalkedPath walkedPath, JObject context) { JToken input = inputOptional; // sanity checks, cannot work on a list spec with map input and vice versa, and runtime with null input if (!_specDataType.IsCompatible(input)) { return; } // create input if it is null if (input == null || input.Type == JTokenType.Null) { input = _specDataType.Create(inputKey, walkedPath, _opMode); // if input has changed, wrap if (input != null && input.Type != JTokenType.Null) { inputOptional = input; } } // if input is List, create special ArrayMatchedElement, which tracks the original size of the input array if (input is JArray list) { // LIST means spec had array index explicitly specified, hence expand if needed if (_specDataType is LIST specList) { int?origSize = specList.Expand(input); thisLevel = new ArrayMatchedElement(thisLevel.RawKey, origSize ?? 0); } else { // specDataType is RUNTIME, so spec had no array index explicitly specified, no need to expand thisLevel = new ArrayMatchedElement(thisLevel.RawKey, list.Count); } } // add self to walked path walkedPath.Add(input, thisLevel); // Handle the rest of the children _executionStrategy.Process(this, inputOptional, walkedPath, null, context); // We are done, so remove ourselves from the walkedPath walkedPath.RemoveLast(); }