private static InputControl MatchByUsageAtDeviceRootRecursive(InputDevice device, string path, int indexInPath,
                                                                      ref ArrayOrListWrapper <InputControl> matches)
        {
            var usages = device.m_UsagesForEachControl;

            if (usages == null)
            {
                return(null);
            }

            var usageCount           = usages.Length;
            var startIndex           = indexInPath + 1;
            var pathCanMatchMultiple = PathComponentCanYieldMultipleMatches(path, indexInPath);
            var pathLength           = path.Length;

            Debug.Assert(path[indexInPath] == '{');
            ++indexInPath;
            if (indexInPath == pathLength)
            {
                throw new Exception(string.Format("Invalid path spec '{0}'; trailing '{{'", path));
            }

            InputControl lastMatch = null;

            for (var i = 0; i < usageCount; ++i)
            {
                var usage = usages[i];

                // Match usage agaist path.
                var usageIsMatch = MatchPathComponent(usage, path, ref indexInPath, PathComponentType.Usage);

                // If it isn't a match, go to next usage.
                if (!usageIsMatch)
                {
                    indexInPath = startIndex;
                    continue;
                }

                var controlMatchedByUsage = device.m_UsageToControl[i];

                // If there's more to go in the path, dive into the children of the control.
                if (indexInPath < pathLength && path[indexInPath] == '/')
                {
                    lastMatch = MatchChildrenRecursive(controlMatchedByUsage, path, indexInPath + 1,
                                                       ref matches);

                    // We can stop going through usages if we matched something and the
                    // path component covering usage does not contain wildcards.
                    if (lastMatch != null && !pathCanMatchMultiple)
                    {
                        break;
                    }

                    // We can stop going through usages if we have a match and are only
                    // looking for a single one.
                    if (lastMatch != null && matches.isNull)
                    {
                        break;
                    }
                }
                else
                {
                    lastMatch = controlMatchedByUsage;
                    if (!matches.isNull)
                    {
                        matches.Add(controlMatchedByUsage);
                    }
                    else
                    {
                        // Only looking for single match and we have one.
                        break;
                    }
                }
            }

            return(lastMatch);
        }
        ////TODO: refactor this to use the new PathParser

        private static InputControl MatchControlsRecursive(InputControl control, string path, int indexInPath, ref ArrayOrListWrapper <InputControl> matches)
        {
            var pathLength = path.Length;

            // Try to get a match. A path spec has three components:
            //    "<layout>{usage}name"
            // All are optional but at least one component must be present.
            // Names can be aliases, too.

            var controlIsMatch = true;

            // Match by layout.
            if (path[indexInPath] == '<')
            {
                ++indexInPath;
                controlIsMatch =
                    MatchPathComponent(control.layout, path, ref indexInPath, PathComponentType.Layout);

                // If the layout isn't a match, walk up the base layout
                // chain and match each base layout.
                if (!controlIsMatch)
                {
                    var baseLayout = control.m_Layout;
                    while (InputControlLayout.s_Layouts.baseLayoutTable.TryGetValue(baseLayout, out baseLayout))
                    {
                        controlIsMatch = MatchPathComponent(baseLayout, path, ref indexInPath,
                                                            PathComponentType.Layout);
                        if (controlIsMatch)
                        {
                            break;
                        }
                    }
                }
            }

            // Match by usage.
            if (indexInPath < pathLength && path[indexInPath] == '{' && controlIsMatch)
            {
                ++indexInPath;

                for (var i = 0; i < control.usages.Count; ++i)
                {
                    controlIsMatch = MatchPathComponent(control.usages[i], path, ref indexInPath, PathComponentType.Usage);
                    if (controlIsMatch)
                    {
                        break;
                    }
                }
            }

            // Match by name.
            if (indexInPath < pathLength && controlIsMatch && path[indexInPath] != '/')
            {
                // Normal name match.
                controlIsMatch = MatchPathComponent(control.name, path, ref indexInPath, PathComponentType.Name);

                // Alternative match by alias.
                if (!controlIsMatch)
                {
                    for (var i = 0; i < control.aliases.Count && !controlIsMatch; ++i)
                    {
                        controlIsMatch = MatchPathComponent(control.aliases[i], path, ref indexInPath,
                                                            PathComponentType.Name);
                    }
                }
            }

            // If we have a match, return it or, if there's children, recurse into them.
            if (controlIsMatch)
            {
                // If we ended up on a wildcard, we've successfully matched it.
                if (indexInPath < pathLength && path[indexInPath] == '*')
                {
                    ++indexInPath;
                }

                // If we've reached the end of the path, we have a match.
                if (indexInPath == pathLength)
                {
                    if (!matches.isNull)
                    {
                        matches.Add(control);
                    }
                    return(control);
                }

                // If we've reached a separator, dive into our children.
                if (path[indexInPath] == '/')
                {
                    ++indexInPath;

                    // Silently accept trailing slashes.
                    if (indexInPath == pathLength)
                    {
                        if (!matches.isNull)
                        {
                            matches.Add(control);
                        }
                        return(control);
                    }

                    // See if we want to match children by usage or by name.
                    InputControl lastMatch = null;
                    if (path[indexInPath] == '{')
                    {
                        ////TODO: support scavenging a subhierarchy for usages
                        if (!ReferenceEquals(control.device, control))
                        {
                            throw new NotImplementedException(
                                      "Matching usages inside subcontrols instead of at device root");
                        }

                        // Usages are kind of like entry points that can route to anywhere else
                        // on a device's control hierarchy and then we keep going from that re-routed
                        // point.
                        lastMatch = MatchByUsageAtDeviceRootRecursive(control.device, path, indexInPath, ref matches);
                    }
                    else
                    {
                        // Go through children and see what we can match.
                        lastMatch = MatchChildrenRecursive(control, path, indexInPath, ref matches);
                    }

                    return(lastMatch);
                }
            }

            return(null);
        }