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);
        }
예제 #2
0
        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));
        }
예제 #5
0
        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));
        }
예제 #8
0
        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));
            }
        }
예제 #9
0
        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);
        }
예제 #10
0
        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);
        }
예제 #12
0
        /**
         * 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();
            }
예제 #13
0
        /**
         * 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());
        }
예제 #14
0
 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);
     }
 }
예제 #15
0
        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);
        }
예제 #16
0
        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));
        }
예제 #19
0
        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));
            }
        }
예제 #22
0
        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.");
            }
        }
예제 #23
0
        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());
        }
예제 #24
0
        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);
        }
예제 #25
0
        /**
         * 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);
        }
예제 #26
0
 /**
  * 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);
     }
 }
예제 #27
0
 /**
  * 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);
예제 #28
0
 public bool Apply(string inputKey, JToken inputOptional, WalkedPath walkedPath, JObject output, JObject context)
 {
     return(ApplyCardinality(inputKey, inputOptional, walkedPath, output));
 }
예제 #29
0
 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();
        }