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); }